< Summary

Information
Class: BallSort.Engine.Game.Board
Assembly: BallSort.Engine
File(s): /home/runner/work/BallSort/BallSort/src/BallSort.Engine/Game/Board.cs
Tag: 216
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
 986455816    public int Width { get; private init; }
 17
 132029418    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    {
 1057858        var source = move.Source;
 59
 1057860        var target = move.Target;
 61
 1057862        Guard(source, "Source column {column} is out of bounds.");
 63
 1057464        if (_columns[source].Count == 0)
 65        {
 266            throw new InvalidMoveException($"Source column {source} is empty. Move id {move.Id}.");
 67        }
 68
 1057269        var sourceBall = _columns[source].Peek();
 70
 1057271        Guard(target,  $"Target column {move.Target} is out of bounds. Move id {move.Id}.");
 72
 1056873        if (Height - _columns[target].Count == 0)
 74        {
 275            throw new InvalidMoveException($"Target column {target} is full. Move id {move.Id}.");
 76        }
 77
 1056678        var targetBall = Top(target);
 79
 1056680        if (targetBall != Colour.Empty && sourceBall != targetBall)
 81        {
 282            throw new InvalidMoveException($"Cannot move {sourceBall.ToHumanReadable()} ball from column {source} onto {
 83        }
 84
 1056485        _columns[target].Push(_columns[source].Pop());
 86
 1056487        _history.Push(new Move(source, target));
 1056488    }
 89
 90    public void Move(int source, int target)
 91    {
 1892        Move(new Move(source, target));
 493    }
 94
 95    public void UndoLastMove()
 96    {
 885697        if (_history.Count > 0)
 98        {
 885699            var lastMove = _history.Pop();
 100
 8856101            _columns[lastMove.Source].Push(_columns[lastMove.Target].Pop());
 102        }
 8856103    }
 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    {
 152368124        Guard(column);
 125
 152366126        var data = new Colour[Height];
 127
 152366128        var i = _columns[column].Count - 1;
 129
 1483984130        foreach (var item in _columns[column])
 131        {
 589626132            data[i] = item;
 133
 589626134            i--;
 135        }
 136
 152366137        return data;
 138    }
 139
 140    public bool IsComplete(int column)
 141    {
 120344142        Guard(column);
 143
 120344144        if (_columns[column].Count != Height)
 145        {
 55546146            return false;
 147        }
 148
 64798149        var colour = _columns[column].Peek();
 150
 553104151        foreach (var item in _columns[column])
 152        {
 239014153            if (item != colour)
 154            {
 54520155                return false;
 156            }
 157        }
 158
 10278159        return true;
 54520160    }
 161
 162    public bool IsEmpty(int column)
 163    {
 811266164        Guard(column);
 165
 811266166        return _columns[column].Count == 0;
 167    }
 168
 169    public int BallCount(int column)
 170    {
 129464171        Guard(column);
 172
 129464173        return _columns[column].Count;
 174    }
 175
 176    public Colour Top(int column)
 177    {
 3547966178        Guard(column);
 179
 3547966180        if (_columns[column].Count == 0)
 181        {
 8802182            return Colour.Empty;
 183        }
 184
 3539164185        return _columns[column].Peek();
 186    }
 187
 188    public int TopRunLength(int column)
 189    {
 71262190        Guard(column);
 191
 71262192        var stack = _columns[column];
 193
 71262194        if (! stack.TryPeek(out var colour))
 195        {
 2196            return 0;
 197        }
 198
 71260199        var length = 1;
 200
 71260201        var enumerator = stack.GetEnumerator();
 202
 71260203        enumerator.MoveNext();
 204
 148934205        while (enumerator.MoveNext())
 206        {
 134868207            if (enumerator.Current != colour)
 208            {
 209                break;
 210            }
 211
 77674212            length++;
 213        }
 214
 71260215        return length;
 216    }
 217
 218    public bool IsFull(int column)
 219    {
 63412220        return Capacity(column) == 0;
 221    }
 222
 223    public bool IsPure(int column)
 224    {
 89514225        Guard(column);
 226
 89514227        var colour = _columns[column].Peek();
 228
 671464229        foreach (var item in _columns[column])
 230        {
 280136231            if (item != colour)
 232            {
 67836233                return false;
 234            }
 235        }
 236
 21678237        return true;
 67836238    }
 239
 240    public int Capacity(int column)
 241    {
 116424242        Guard(column);
 243
 116424244        return Height - _columns[column].Count;
 245    }
 246
 247    public bool IsSolved()
 248    {
 23404249        for (var x = 0; x < Width; x++)
 250        {
 11678251            if (IsEmpty(x))
 252            {
 253                continue;
 254            }
 255
 11572256            if (! IsComplete(x))
 257            {
 10536258                return false;
 259            }
 260        }
 261
 24262        return true;
 263    }
 264
 265    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 266    private void Guard(int column, string messageTemplate = null)
 267    {
 5059758268        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        }
 5059748277    }
 278}