| | 1 | | using System.Security.Cryptography; |
| | 2 | | using System.Text; |
| | 3 | | using LanguageExt; |
| | 4 | | using LanguageExt.Common; |
| | 5 | | using static LanguageExt.Prelude; |
| | 6 | | using CellMap = LanguageExt.HashMap<(int, int), Minesweeper.Cell>; |
| | 7 | |
|
| | 8 | | namespace Minesweeper; |
| | 9 | |
|
| | 10 | | public record MineField |
| | 11 | | { |
| | 12 | |
|
| 5 | 13 | | public record Setup(int Width, int Height) : MineField; |
| 17 | 14 | | public record SetupWithBombs(int Width, int Height, int Bombs) : MineField; |
| 66 | 15 | | public record SetupWithBombsPos(int Width, int Height, IEnumerable<(int X, int Y)> Bombs) : MineField; |
| 162 | 16 | | public record Playing(int Width, int Height, CellMap Cells) : MineField; |
| | 17 | | public record Win : MineField; |
| | 18 | | public record Loose : MineField; |
| | 19 | |
|
| | 20 | | public string ToInnerStr() => this switch |
| | 21 | | { |
| 1 | 22 | | Playing x => (from b in Enumerable.Range(0, x.Height) |
| 3 | 23 | | from a in Enumerable.Range(0, x.Width) |
| 9 | 24 | | select (a, b)) |
| 9 | 25 | | .OrderBy(x => x) |
| 9 | 26 | | .Fold(new StringBuilder(), (s, e) => s.Append(x.Cells[e].ToInnerChar())) |
| 1 | 27 | | .ToString() |
| | 28 | | }; |
| | 29 | |
|
| | 30 | | public string ToStr() => this switch |
| | 31 | | { |
| 1 | 32 | | Playing x => (from b in Enumerable.Range(0, x.Height) |
| 3 | 33 | | from a in Enumerable.Range(0, x.Width) |
| 9 | 34 | | select (a, b)) |
| 9 | 35 | | .Fold(new StringBuilder(), (s, e) => s.Append(x.Cells[e].ToChar())) |
| 1 | 36 | | .ToString() |
| | 37 | | }; |
| | 38 | |
|
| 15 | 39 | | public CellMap? ToCells() => this switch |
| 15 | 40 | | { |
| 10 | 41 | | Playing x => x.Cells, |
| 15 | 42 | |
|
| 5 | 43 | | _ => null |
| 15 | 44 | | }; |
| | 45 | |
|
| 54 | 46 | | public MineField ClickTo(int xPos, int yPos) => this switch |
| 54 | 47 | | { |
| 0 | 48 | | Setup x => x.StartTo().ClickTo(xPos, yPos), |
| 54 | 49 | |
|
| 3 | 50 | | SetupWithBombsPos x => x.StartTo().ClickTo(xPos, yPos), |
| 54 | 51 | |
|
| 0 | 52 | | SetupWithBombs x => x.StartTo().ClickTo(xPos, yPos), |
| 54 | 53 | |
|
| 42 | 54 | | Playing x => x.Cells.ContainsKey((xPos, yPos)) ? x.Cells[(xPos, yPos)] is Cell.Covered ? Id( |
| 42 | 55 | | from _1 in Id(x with |
| 42 | 56 | | { |
| 13 | 57 | | Cells = x.Cells.AddOrUpdate((xPos, yPos), y => y.ClickTo(), new Cell.Empty()) |
| 42 | 58 | | }) |
| 13 | 59 | | let _2 = _1.Cells[(xPos, yPos)] switch |
| 13 | 60 | | { |
| 1 | 61 | | Cell.Bomb => new Loose(), |
| 6 | 62 | | Cell.Number { Value: 0 } => _1.ClickTo(xPos - 1, yPos - 1) |
| 6 | 63 | | .ClickTo(xPos, yPos - 1) |
| 6 | 64 | | .ClickTo(xPos + 1, yPos - 1) |
| 6 | 65 | | .ClickTo(xPos - 1, yPos) |
| 6 | 66 | | .ClickTo(xPos + 1, yPos) |
| 6 | 67 | | .ClickTo(xPos - 1, yPos + 1) |
| 6 | 68 | | .ClickTo(xPos, yPos + 1) |
| 6 | 69 | | .ClickTo(xPos + 1, yPos + 1), |
| 6 | 70 | | _ => _1 |
| 13 | 71 | | } |
| 26 | 72 | | select _2).Value.Map(a => a.ToCells()? |
| 116 | 73 | | .Filter((k, v) => v is Cell.Covered) |
| 24 | 74 | | .Exists((k, v) => v.IsBomb() is not true) ?? true ? a : new Win()).Value |
| 42 | 75 | | : x : x, |
| 54 | 76 | |
|
| 9 | 77 | | var x => x |
| 54 | 78 | | }; |
| | 79 | |
|
| | 80 | | public MineField StartTo() => this switch |
| | 81 | | { |
| 6 | 82 | | SetupWithBombsPos x => new Playing(x.Width, x.Height, Id(() => |
| 6 | 83 | | { |
| 6 | 84 | | var q = new CellMap(from b in Enumerable.Range(0, x.Height) |
| 18 | 85 | | from a in Enumerable.Range(0, x.Width) |
| 60 | 86 | | select ((a, b), Cell.New())); |
| 6 | 87 | |
|
| 24 | 88 | | return x.Bombs.Fold(q, (s, e) => s.AddOrUpdate(e, x => x.BombTo(), new Cell.Empty()) |
| 6 | 89 | | .AddOrUpdate((e.X - 1, e.Y - 1), x => x.AddNumberTo(), new Cell.Empty()) |
| 7 | 90 | | .AddOrUpdate((e.X , e.Y - 1), x => x.AddNumberTo(), new Cell.Empty()) |
| 4 | 91 | | .AddOrUpdate((e.X + 1, e.Y - 1), x => x.AddNumberTo(), new Cell.Empty()) |
| 5 | 92 | | .AddOrUpdate((e.X - 1, e.Y), x => x.AddNumberTo(), new Cell.Empty()) |
| 6 | 93 | | .AddOrUpdate((e.X + 1, e.Y), x => x.AddNumberTo(), new Cell.Empty()) |
| 1 | 94 | | .AddOrUpdate((e.X - 1, e.Y + 1), x => x.AddNumberTo(), new Cell.Empty()) |
| 5 | 95 | | .AddOrUpdate((e.X , e.Y + 1), x => x.AddNumberTo(), new Cell.Empty()) |
| 19 | 96 | | .AddOrUpdate((e.X + 1, e.Y + 1), x => x.AddNumberTo(), new Cell.Empty())); |
| 12 | 97 | | }).Value()), |
| | 98 | |
|
| 1 | 99 | | Setup x => new SetupWithBombs(x.Width, x.Height, 0).StartTo(), |
| | 100 | |
|
| 2 | 101 | | SetupWithBombs x => Id(() => |
| 2 | 102 | | { |
| 2 | 103 | | var bombPos = (from j in RandomGenerator(x.Height) |
| 1 | 104 | | from i in RandomGenerator(x.Width) |
| 6 | 105 | | select (i, j)).Distinct().Take(x.Bombs); |
| 2 | 106 | |
|
| 2 | 107 | | return new SetupWithBombsPos(x.Width, x.Height, bombPos); |
| 2 | 108 | |
|
| 2 | 109 | | static IEnumerable<int> RandomGenerator(int max) |
| 2 | 110 | | { |
| 5 | 111 | | while (true) |
| 5 | 112 | | { |
| 5 | 113 | | yield return RandomNumberGenerator.GetInt32(max); |
| 3 | 114 | | } |
| 2 | 115 | | } |
| 4 | 116 | | }).Value().StartTo(), |
| | 117 | |
|
| 0 | 118 | | var x => x |
| | 119 | | }; |
| | 120 | | } |
| | 121 | |
|