< Summary

Information
Class: BallSort.Engine.Game.Board
Assembly: BallSort.Engine
File(s): /home/runner/work/BallSort/BallSort/src/BallSort.Engine/Game/Board.cs
Tag: 238
Line coverage
100%
Covered lines: 106
Uncovered lines: 0
Coverable lines: 106
Total lines: 278
Line coverage: 100%
Branch coverage
100%
Covered branches: 48
Total branches: 48
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
get_Width()100%11100%
get_Height()100%11100%
get_Colours()100%11100%
.ctor(...)100%66100%
Move(...)100%88100%
Move(...)100%11100%
UndoLastMove()100%22100%
Clone()100%22100%
GetColumn(...)100%22100%
IsComplete(...)100%66100%
IsEmpty(...)100%11100%
BallCount(...)100%11100%
Top(...)100%22100%
TopRunLength(...)100%66100%
IsFull(...)100%11100%
IsPure(...)100%44100%
Capacity(...)100%11100%
IsSolved()100%66100%
Guard(...)100%44100%

File(s)

/home/runner/work/BallSort/BallSort/src/BallSort.Engine/Game/Board.cs

#LineLine coverage
 1using System.Runtime.CompilerServices;
 2using BallSort.Engine.Exceptions;
 3using BallSort.Engine.Extensions;
 4using BallSort.Engine.Models;
 5
 6namespace BallSort.Engine.Game;
 7
 8public class Board
 9{
 10    private Stack<Colour>[] _columns;
 11
 15212    private readonly HashSet<Colour> _colours = [];
 13
 15214    private readonly Stack<Move> _history = [];
 15
 1036177416    public int Width { get; private init; }
 17
 219502818    public int Height { get; private init; }
 19
 220    public int Colours => _colours.Count;
 21
 2822    private Board()
 23    {
 2824    }
 25
 12426    public Board(Puzzle puzzle)
 27    {
 12428        _columns = new Stack<Colour>[puzzle.GridWidth];
 29
 12430        var index = 0;
 31
 12432        Width = puzzle.GridWidth;
 33
 12434        Height = puzzle.GridHeight;
 35
 199236        for (var column = 0; column < Width; column++)
 37        {
 87238            _columns[column] = new Stack<Colour>(Height);
 39
 838440            for (var row = 0; row < Height; row++)
 41            {
 332042                var ball = (Colour) puzzle.Data.Layout[index];
 43
 332044                if (ball != Colour.Empty)
 45                {
 252646                    _columns[column].Push(ball);
 47
 252648                    _colours.Add(ball);
 49                }
 50
 332051                index++;
 52            }
 53        }
 12454    }
 55
 56    public void Move(Move move)
 57    {
 1049658        var source = move.Source;
 59
 1049660        var target = move.Target;
 61
 1049662        Guard(source, "Source column {column} is out of bounds.");
 63
 1049264        if (_columns[source].Count == 0)
 65        {
 266            throw new InvalidMoveException($"Source column {source} is empty. Move id {move.Id}.");
 67        }
 68
 1049069        var sourceBall = _columns[source].Peek();
 70
 1049071        Guard(target,  $"Target column {move.Target} is out of bounds. Move id {move.Id}.");
 72
 1048673        if (Height - _columns[target].Count == 0)
 74        {
 275            throw new InvalidMoveException($"Target column {target} is full. Move id {move.Id}.");
 76        }
 77
 1048478        var targetBall = Top(target);
 79
 1048480        if (targetBall != Colour.Empty && sourceBall != targetBall)
 81        {
 282            throw new InvalidMoveException($"Cannot move {sourceBall.ToHumanReadable()} ball from column {source} onto {
 83        }
 84
 1048285        _columns[target].Push(_columns[source].Pop());
 86
 1048287        _history.Push(new Move(source, target));
 1048288    }
 89
 90    public void Move(int source, int target)
 91    {
 1892        Move(new Move(source, target));
 493    }
 94
 95    public void UndoLastMove()
 96    {
 877497        if (_history.Count > 0)
 98        {
 877499            var lastMove = _history.Pop();
 100
 8774101            _columns[lastMove.Source].Push(_columns[lastMove.Target].Pop());
 102        }
 8774103    }
 104
 105    public Board Clone()
 106    {
 28107        var board = new Board
 28108        {
 28109            _columns = new Stack<Colour>[Width],
 28110            Width = Width,
 28111            Height = Height
 28112        };
 113
 672114        for (var column = 0; column < _columns.Length; column++)
 115        {
 308116            board._columns[column] = new Stack<Colour>(_columns[column].Reverse());
 117        }
 118
 28119        return board;
 120    }
 121
 122    public ReadOnlySpan<Colour> GetColumn(int column)
 123    {
 151342124        Guard(column);
 125
 151340126        var data = new Colour[Height];
 127
 151340128        var i = _columns[column].Count - 1;
 129
 1474660130        foreach (var item in _columns[column])
 131        {
 585990132            data[i] = item;
 133
 585990134            i--;
 135        }
 136
 151340137        return data;
 138    }
 139
 140    public bool IsComplete(int column)
 141    {
 129160142        Guard(column);
 143
 129160144        if (_columns[column].Count != Height)
 145        {
 64456146            return false;
 147        }
 148
 64704149        var colour = _columns[column].Peek();
 150
 552236151        foreach (var item in _columns[column])
 152        {
 238628153            if (item != colour)
 154            {
 54428155                return false;
 156            }
 157        }
 158
 10276159        return true;
 54428160    }
 161
 162    public bool IsEmpty(int column)
 163    {
 284514164        Guard(column);
 165
 284514166        return _columns[column].Count == 0;
 167    }
 168
 169    public int BallCount(int column)
 170    {
 129428171        Guard(column);
 172
 129428173        return _columns[column].Count;
 174    }
 175
 176    public Colour Top(int column)
 177    {
 3611398178        Guard(column);
 179
 3611398180        if (_columns[column].Count == 0)
 181        {
 8862182            return Colour.Empty;
 183        }
 184
 3602536185        return _columns[column].Peek();
 186    }
 187
 188    public int TopRunLength(int column)
 189    {
 80172190        Guard(column);
 191
 80172192        var stack = _columns[column];
 193
 80172194        if (! stack.TryPeek(out var colour))
 195        {
 2196            return 0;
 197        }
 198
 80170199        var length = 1;
 200
 80170201        var enumerator = stack.GetEnumerator();
 202
 80170203        enumerator.MoveNext();
 204
 160420205        while (enumerator.MoveNext())
 206        {
 148392207            if (enumerator.Current != colour)
 208            {
 209                break;
 210            }
 211
 80250212            length++;
 213        }
 214
 80170215        return length;
 216    }
 217
 218    public bool IsFull(int column)
 219    {
 26962220        return Capacity(column) == 0;
 221    }
 222
 223    public bool IsPure(int column)
 224    {
 105744225        Guard(column);
 226
 105744227        var colour = _columns[column].Peek();
 228
 779856229        foreach (var item in _columns[column])
 230        {
 325354231            if (item != colour)
 232            {
 82340233                return false;
 234            }
 235        }
 236
 23404237        return true;
 82340238    }
 239
 240    public int Capacity(int column)
 241    {
 981552242        Guard(column);
 243
 981552244        return Height - _columns[column].Count;
 245    }
 246
 247    public bool IsSolved()
 248    {
 23240249        for (var x = 0; x < Width; x++)
 250        {
 11596251            if (IsEmpty(x))
 252            {
 253                continue;
 254            }
 255
 11490256            if (! IsComplete(x))
 257            {
 10454258                return false;
 259            }
 260        }
 261
 24262        return true;
 263    }
 264
 265    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 266    private void Guard(int column, string messageTemplate = null)
 267    {
 5494296268        if ((uint) column >= (uint) Width)
 269        {
 10270            if (messageTemplate == null)
 271            {
 2272                throw new OutOfBoundsException($"Column {column} is out of bounds.");
 273            }
 274
 8275            throw new OutOfBoundsException(messageTemplate.Replace("{column}", column.ToString()));
 276        }
 5494286277    }
 278}