no idea speedrun

This commit is contained in:
Kecskeméti László 2024-10-23 23:10:30 +02:00
parent e2c9116575
commit a2726c715c
6 changed files with 61 additions and 55 deletions

View File

@ -163,7 +163,7 @@ public class SnakeLevel : IDisposable{
#endregion #endregion
#region Event handlers #region Event handlers
private void _timer_Tick(object? sender) private void _timer_Tick(object? state)
{ {
Tick(); Tick();
GameUpdate?.Invoke(this, EventArgs.Empty); GameUpdate?.Invoke(this, EventArgs.Empty);
@ -171,7 +171,7 @@ public class SnakeLevel : IDisposable{
#endregion #endregion
#region Methods #region Methods
public void Tick(){ private void Tick(){
if (!_disable_snake_movement) if (!_disable_snake_movement)
{ {
Point new_snake_head = (0, 0); Point new_snake_head = (0, 0);
@ -279,10 +279,10 @@ public class SnakeLevel : IDisposable{
#endregion #endregion
#region IDisposable #region IDisposable
private bool disposedValue; private bool _disposed_value;
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!disposedValue) if (!_disposed_value)
{ {
if (disposing) if (disposing)
{ {
@ -292,7 +292,7 @@ public class SnakeLevel : IDisposable{
// TODO: free unmanaged resources (unmanaged objects) and override finalizer // TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null // TODO: set large fields to null
disposedValue = true; _disposed_value = true;
} }
} }

View File

@ -5,28 +5,33 @@ using Snake.Model;
namespace Snake.Persistance; namespace Snake.Persistance;
public class SnakeLevelLoader{ public class SnakeLevelLoader{
#region Fields
private readonly string SaveFileName;
private List<StoredSnakeLevel>? _stored_levels;
#endregion
public readonly string SaveFileName; #region Properties
public ReadOnlyCollection<StoredSnakeLevel> StoredLevels => _stored_levels?.AsReadOnly()
?? throw new InvalidOperationException("Can not query stored levels before loading the data");
#endregion
#region Constructors
public SnakeLevelLoader(string saveFileName){ public SnakeLevelLoader(string saveFileName){
SaveFileName = saveFileName; SaveFileName = saveFileName;
if(!File.Exists(SaveFileName)){ if(!File.Exists(SaveFileName)){
throw new FileNotFoundException("Save file not found", SaveFileName); throw new FileNotFoundException("Save file not found", SaveFileName);
} }
} }
#endregion
#region Methods
public async Task ReadAllDataAsync() public async Task ReadAllDataAsync()
{ {
string file_content = await File.ReadAllTextAsync(SaveFileName); string file_content = await File.ReadAllTextAsync(SaveFileName);
_stored_levels = JsonSerializer.Deserialize<List<StoredSnakeLevel>>(file_content, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower }) _stored_levels = JsonSerializer.Deserialize<List<StoredSnakeLevel>>(file_content, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower })
?? throw new JsonException("Failed to deserialize stored levels"); ?? throw new JsonException("Failed to deserialize stored levels");
await Task.Delay(2000);
} }
private List<StoredSnakeLevel>? _stored_levels;
public ReadOnlyCollection<StoredSnakeLevel> StoredLevels => _stored_levels?.AsReadOnly()
?? throw new InvalidOperationException("Can not query stored levels before loading the data");
public SnakeLevel LoadLevel(StoredSnakeLevel level){ public SnakeLevel LoadLevel(StoredSnakeLevel level){
return new SnakeLevel(level.Size, return new SnakeLevel(level.Size,
level.Obstacles.Select(a => new Point(a[0], a[1])), level.Obstacles.Select(a => new Point(a[0], a[1])),
@ -34,4 +39,5 @@ public class SnakeLevelLoader{
level.NewEggRound, level.NewEggRound,
level.EggLimit); level.EggLimit);
} }
#endregion
} }

View File

@ -1,6 +1,6 @@
namespace view namespace view
{ {
partial class Form1 partial class MainForm
{ {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.

View File

@ -3,19 +3,19 @@ using Snake.Persistance;
namespace view namespace view
{ {
public partial class Form1 : Form public partial class MainForm : Form
{ {
#region Fields #region Fields
const int line_width = 2; // Width of the table lines const int line_width = 2; // Width of the table lines
SnakeLevel? CurrentLevel; // Current game state SnakeLevel? _current_level; // Current game state
SnakeLevelLoader level_loader; // Level loader SnakeLevelLoader _level_loader; // Level loader
#endregion #endregion
#region Constructor #region Constructor
public Form1() public MainForm()
{ {
InitializeComponent(); InitializeComponent();
level_loader = new SnakeLevelLoader(Path.Combine(Directory.GetCurrentDirectory(), "levels.json")); _level_loader = new SnakeLevelLoader(Path.Combine(Directory.GetCurrentDirectory(), "levels.json"));
} }
#endregion #endregion
@ -25,7 +25,7 @@ namespace view
/// </summary> /// </summary>
private void Draw() private void Draw()
{ {
if (CurrentLevel == null) return; if (_current_level == null) return;
using Graphics canvas_graphics = Canvas.CreateGraphics(); using Graphics canvas_graphics = Canvas.CreateGraphics();
@ -35,31 +35,31 @@ namespace view
graphics.Clear(Color.White); graphics.Clear(Color.White);
var canvas_size = Math.Min(Canvas.Width, Canvas.Height); var canvas_size = Math.Min(Canvas.Width, Canvas.Height);
double cell_size = (double)(canvas_size - (CurrentLevel.Size + 1) * line_width) / CurrentLevel.Size; double cell_size = (double)(canvas_size - (_current_level.Size + 1) * line_width) / _current_level.Size;
// vertical lines // vertical lines
for (int i = 0; i <= CurrentLevel.Size; i++) for (int i = 0; i <= _current_level.Size; i++)
{ {
int x = (int)(i * (cell_size + line_width)); int x = (int)(i * (cell_size + line_width));
graphics.FillRectangle(Brushes.Black, x, 0, line_width, canvas_size); graphics.FillRectangle(Brushes.Black, x, 0, line_width, canvas_size);
} }
// horizontal lines // horizontal lines
for (int i = 0; i <= CurrentLevel.Size; i++) for (int i = 0; i <= _current_level.Size; i++)
{ {
int y = (int)(i * (cell_size + line_width)); int y = (int)(i * (cell_size + line_width));
graphics.FillRectangle(Brushes.Black, 0, y, canvas_size, line_width); graphics.FillRectangle(Brushes.Black, 0, y, canvas_size, line_width);
} }
// Ssnake // Ssnake
foreach (var pos in CurrentLevel.Snake) foreach (var pos in _current_level.Snake)
{ {
int x = (int)(cell_size * pos.X + line_width * (pos.X + 1)); int x = (int)(cell_size * pos.X + line_width * (pos.X + 1));
int y = (int)(cell_size * pos.Y + line_width * (pos.Y + 1)); int y = (int)(cell_size * pos.Y + line_width * (pos.Y + 1));
graphics.FillRectangle(Brushes.Green, x, y, (int)cell_size, (int)cell_size); graphics.FillRectangle(Brushes.Green, x, y, (int)cell_size, (int)cell_size);
} }
// snake head // snake head
int snake_head_x = (int)(cell_size * CurrentLevel.SnakeHead.X + line_width * (CurrentLevel.SnakeHead.X + 1)); int snake_head_x = (int)(cell_size * _current_level.SnakeHead.X + line_width * (_current_level.SnakeHead.X + 1));
int snake_head_y = (int)(cell_size * CurrentLevel.SnakeHead.Y + line_width * (CurrentLevel.SnakeHead.Y + 1)); int snake_head_y = (int)(cell_size * _current_level.SnakeHead.Y + line_width * (_current_level.SnakeHead.Y + 1));
graphics.FillRectangle(Brushes.Red, snake_head_x, snake_head_y, (int)cell_size, (int)cell_size); graphics.FillRectangle(Brushes.Red, snake_head_x, snake_head_y, (int)cell_size, (int)cell_size);
switch (CurrentLevel.SnakeHeadDirection) switch (_current_level.SnakeHeadDirection)
{ {
case SnakeLevel.SnakeDirection.Up: case SnakeLevel.SnakeDirection.Up:
graphics.DrawLine(Pens.Black, snake_head_x + (int)cell_size / 2, snake_head_y, snake_head_x + (int)cell_size / 2, snake_head_y + (int)cell_size); graphics.DrawLine(Pens.Black, snake_head_x + (int)cell_size / 2, snake_head_y, snake_head_x + (int)cell_size / 2, snake_head_y + (int)cell_size);
@ -85,7 +85,7 @@ namespace view
} }
// obstacles // obstacles
foreach (var pos in CurrentLevel.Obstacles) foreach (var pos in _current_level.Obstacles)
{ {
int x = (int)(cell_size * pos.X + line_width * (pos.X + 1)); int x = (int)(cell_size * pos.X + line_width * (pos.X + 1));
int y = (int)(cell_size * pos.Y + line_width * (pos.Y + 1)); int y = (int)(cell_size * pos.Y + line_width * (pos.Y + 1));
@ -93,7 +93,7 @@ namespace view
} }
//eggs //eggs
foreach (var pos in CurrentLevel.Eggs) foreach (var pos in _current_level.Eggs)
{ {
int x = (int)(cell_size * pos.X + line_width * (pos.X + 1)); int x = (int)(cell_size * pos.X + line_width * (pos.X + 1));
int y = (int)(cell_size * pos.Y + line_width * (pos.Y + 1)); int y = (int)(cell_size * pos.Y + line_width * (pos.Y + 1));
@ -107,24 +107,24 @@ namespace view
#region Form event handlers #region Form event handlers
private void Form1_KeyDown(object sender, KeyEventArgs e) private void Form1_KeyDown(object sender, KeyEventArgs e)
{ {
if (CurrentLevel == null) return; if (_current_level == null) return;
switch (e.KeyCode) switch (e.KeyCode)
{ {
case Keys.Up: case Keys.Up:
CurrentLevel.SnakeHeadDirection = SnakeLevel.SnakeDirection.Up; _current_level.SnakeHeadDirection = SnakeLevel.SnakeDirection.Up;
break; break;
case Keys.Down: case Keys.Down:
CurrentLevel.SnakeHeadDirection = SnakeLevel.SnakeDirection.Down; _current_level.SnakeHeadDirection = SnakeLevel.SnakeDirection.Down;
break; break;
case Keys.Left: case Keys.Left:
CurrentLevel.SnakeHeadDirection = SnakeLevel.SnakeDirection.Left; _current_level.SnakeHeadDirection = SnakeLevel.SnakeDirection.Left;
break; break;
case Keys.Right: case Keys.Right:
CurrentLevel.SnakeHeadDirection = SnakeLevel.SnakeDirection.Right; _current_level.SnakeHeadDirection = SnakeLevel.SnakeDirection.Right;
break; break;
case Keys.Escape: case Keys.Escape:
if (CurrentLevel.State == SnakeLevel.GameState.Running) if (_current_level.State == SnakeLevel.GameState.Running)
CurrentLevel.Pause(); _current_level.Pause();
break; break;
} }
@ -132,31 +132,31 @@ namespace view
private void btnStart_Click(object sender, EventArgs e) private void btnStart_Click(object sender, EventArgs e)
{ {
if (CurrentLevel is null || CurrentLevel.State == SnakeLevel.GameState.Dead) if (_current_level is null || _current_level.State == SnakeLevel.GameState.Dead)
{ {
var MapChooser = new MapChooser(this.level_loader); var MapChooser = new MapChooser(this._level_loader);
MapChooser.ShowDialog(); MapChooser.ShowDialog();
if (MapChooser.SelectedLevel != null) if (MapChooser.SelectedLevel != null)
{ {
CurrentLevel = level_loader.LoadLevel(MapChooser.SelectedLevel); _current_level = _level_loader.LoadLevel(MapChooser.SelectedLevel);
CurrentLevel.GameStateUpdate += CurrentLevel_GameStateUpdate; _current_level.GameStateUpdate += CurrentLevel_GameStateUpdate;
CurrentLevel.GameUpdate += CurrentLevel_GameUpdate; _current_level.GameUpdate += CurrentLevel_GameUpdate;
} }
else else
{ {
CurrentLevel = null; _current_level = null;
} }
} }
if (CurrentLevel != null) if (_current_level != null)
{ {
CurrentLevel.Start(); _current_level.Start();
this.Focus(); this.Focus();
} }
} }
private void Form1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) private void Form1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{ {
if (CurrentLevel?.State is SnakeLevel.GameState.Running) if (_current_level?.State is SnakeLevel.GameState.Running)
{ {
e.IsInputKey = true; e.IsInputKey = true;
} }
@ -165,7 +165,7 @@ namespace view
private void Form1_Shown(object sender, EventArgs e) private void Form1_Shown(object sender, EventArgs e)
{ {
btnEndGame.Visible = false; // It will trigger a whole repaint the first time it is shown unless this is done btnEndGame.Visible = false; // It will trigger a whole repaint the first time it is shown unless this is done
level_loader.ReadAllDataAsync().ContinueWith((task) => _level_loader.ReadAllDataAsync().ContinueWith((task) =>
{ {
if (!this.IsDisposed) // The form can be closed before the file is loaded if (!this.IsDisposed) // The form can be closed before the file is loaded
{ {
@ -188,7 +188,7 @@ namespace view
private void button1_Click(object sender, EventArgs e) private void button1_Click(object sender, EventArgs e)
{ {
CurrentLevel = null; _current_level = null;
btnEndGame.Visible = false; btnEndGame.Visible = false;
Canvas.Refresh(); Canvas.Refresh();
txtScore.Text = ""; txtScore.Text = "";
@ -196,13 +196,13 @@ namespace view
private void Form1_FormClosing(object sender, FormClosingEventArgs e) private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{ {
if (CurrentLevel != null) if (_current_level != null)
{ {
if (CurrentLevel.State == SnakeLevel.GameState.Running) if (_current_level.State == SnakeLevel.GameState.Running)
{ {
CurrentLevel.Pause(); _current_level.Pause();
} }
CurrentLevel = null; _current_level = null;
} }
} }
@ -211,17 +211,17 @@ namespace view
#region Game event handlers #region Game event handlers
private void CurrentLevel_GameUpdate(object? sender, EventArgs e) private void CurrentLevel_GameUpdate(object? sender, EventArgs e)
{ {
if (CurrentLevel == null) return; if (_current_level == null) return;
this.Invoke(() => this.Invoke(() =>
{ {
Draw(); Draw();
txtScore.Text = CurrentLevel.Score.ToString(); txtScore.Text = _current_level.Score.ToString();
}); });
} }
private void CurrentLevel_GameStateUpdate(object? sender, SnakeLevel.GameState e) private void CurrentLevel_GameStateUpdate(object? sender, SnakeLevel.GameState e)
{ {
if (CurrentLevel == null) return; if (_current_level == null) return;
this.Invoke(() => this.Invoke(() =>
{ {
switch (e) switch (e)
@ -237,7 +237,7 @@ namespace view
break; break;
case SnakeLevel.GameState.Dead: case SnakeLevel.GameState.Dead:
btnStart.Visible = true; btnStart.Visible = true;
MessageBox.Show($"Skill issue.\nScore: {CurrentLevel.Score}", "Snake", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show($"Skill issue.\nScore: {_current_level.Score}", "Snake", MessageBoxButtons.OK, MessageBoxIcon.Information);
break; break;
} }
}); });

View File

@ -11,7 +11,7 @@ namespace view
// To customize application configuration such as set high DPI settings or default font, // To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration. // see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize(); ApplicationConfiguration.Initialize();
Application.Run(new Form1()); Application.Run(new MainForm());
} }
} }
} }