< Summary

Information
Class: BallSort.Engine.Game.Board
Assembly: BallSort.Engine
File(s): /home/runner/work/BallSort/BallSort/src/BallSort.Engine/Game/Board.cs
Tag: 158
Line coverage
100%
Covered lines: 97
Uncovered lines: 0
Coverable lines: 97
Total lines: 260
Line coverage: 100%
Branch coverage
100%
Covered branches: 46
Total branches: 46
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%1010100%
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%
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
 9612    private readonly HashSet<Colour> _colours = [];
 13
 14    private Move? _lastMove;
 15
 25094416    public int Width { get; private init; }
 17
 10229018    public int Height { get; private init; }
 19
 220    public int Colours => _colours.Count;
 21
 1022    private Board()
 23    {
 1024    }
 25
 8626    public Board(Puzzle puzzle)
 27    {
 8628        _columns = new Stack<Colour>[puzzle.GridWidth];
 29
 8630        var index = 0;
 31
 8632        Width = puzzle.GridWidth;
 33
 8634        Height = puzzle.GridHeight;
 35
 94036        for (var column = 0; column < Width; column++)
 37        {
 38438            _columns[column] = new Stack<Colour>(Height);
 39
 324840            for (var row = 0; row < Height; row++)
 41            {
 124042                var ball = (Colour) puzzle.Data.Layout[index];
 43
 124044                if (ball != Colour.Empty)
 45                {
 75846                    _columns[column].Push(ball);
 47
 75848                    _colours.Add(ball);
 49                }
 50
 124051                index++;
 52            }
 53        }
 8654    }
 55
 56    public void Move(Move move, bool force = false)
 57    {
 80458        var source = move.Source;
 59
 80460        var target = move.Target;
 61
 80462        Guard(source, "Source column {column} is out of bounds.");
 63
 80064        if (_columns[source].Count == 0)
 65        {
 266            throw new InvalidMoveException($"Source column {source} is empty. Move id {move.Id}.");
 67        }
 68
 79869        var sourceBall = _columns[source].Peek();
 70
 79871        Guard(target,  $"Target column {move.Target} is out of bounds. Move id {move.Id}.");
 72
 79473        if (Height - _columns[target].Count == 0)
 74        {
 275            throw new InvalidMoveException($"Target column {target} is full. Move id {move.Id}.");
 76        }
 77
 79278        var targetBall = Top(target);
 79
 79280        if (! force && targetBall != Colour.Empty && sourceBall != targetBall)
 81        {
 282            throw new InvalidMoveException($"Cannot move {sourceBall.ToHumanReadable()} ball from column {source} onto {
 83        }
 84
 79085        _columns[target].Push(_columns[source].Pop());
 86
 79087        _lastMove = new Move(source, target);
 88
 79089    }
 90
 91    public void Move(int source, int target, bool force = false)
 92    {
 22893        Move(new Move(source, target), force);
 21494    }
 95
 96    public void UndoLastMove()
 97    {
 21098        if (_lastMove.HasValue)
 99        {
 210100            Move(_lastMove.Value.Target, _lastMove.Value.Source, true);
 101        }
 210102    }
 103
 104    public Board Clone()
 105    {
 10106        var board = new Board
 10107        {
 10108            _columns = new Stack<Colour>[Width],
 10109            Width = Width,
 10110            Height = Height
 10111        };
 112
 156113        for (var column = 0; column < _columns.Length; column++)
 114        {
 68115            board._columns[column] = new Stack<Colour>(_columns[column].Reverse());
 116        }
 117
 10118        return board;
 119    }
 120
 121    public Colour[] GetColumn(int column)
 122    {
 5586123        Guard(column);
 124
 5584125        var data = new Colour[Height];
 126
 5584127        var i = _columns[column].Count - 1;
 128
 46300129        foreach (var item in _columns[column])
 130        {
 17566131            data[i] = item;
 132
 17566133            i--;
 134        }
 135
 5584136        return data;
 137    }
 138
 139    public bool IsComplete(int column)
 140    {
 11990141        Guard(column);
 142
 11990143        if (_columns[column].Count != Height)
 144        {
 9542145            return false;
 146        }
 147
 2448148        var colour = _columns[column].Peek();
 149
 22242150        foreach (var item in _columns[column])
 151        {
 9044152            if (item != colour)
 153            {
 742154                return false;
 155            }
 156        }
 157
 1706158        return true;
 742159    }
 160
 161    public bool IsEmpty(int column)
 162    {
 52472163        Guard(column);
 164
 52472165        return _columns[column].Count == 0;
 166    }
 167
 168    public int BallCount(int column)
 169    {
 2164170        Guard(column);
 171
 2164172        return _columns[column].Count;
 173    }
 174
 175    public Colour Top(int column)
 176    {
 28864177        Guard(column);
 178
 28864179        if (_columns[column].Count == 0)
 180        {
 854181            return Colour.Empty;
 182        }
 183
 28010184        return _columns[column].Peek();
 185    }
 186
 187    public int TopRunLength(int column)
 188    {
 1512189        Guard(column);
 190
 1512191        var stack = _columns[column];
 192
 1512193        if (! stack.TryPeek(out var colour))
 194        {
 2195            return 0;
 196        }
 197
 1510198        var length = 1;
 199
 1510200        var enumerator = stack.GetEnumerator();
 201
 1510202        enumerator.MoveNext();
 203
 2020204        while (enumerator.MoveNext())
 205        {
 834206            if (enumerator.Current != colour)
 207            {
 208                break;
 209            }
 210
 510211            length++;
 212        }
 213
 1510214        return length;
 215    }
 216
 217    public bool IsFull(int column)
 218    {
 35200219        return Capacity(column) == 0;
 220    }
 221
 222    public int Capacity(int column)
 223    {
 51704224        Guard(column);
 225
 51704226        return Height - _columns[column].Count;
 227    }
 228
 229    public bool IsSolved()
 230    {
 2436231        for (var x = 0; x < Width; x++)
 232        {
 1212233            if (IsEmpty(x))
 234            {
 235                continue;
 236            }
 237
 1126238            if (! IsComplete(x))
 239            {
 570240                return false;
 241            }
 242        }
 243
 6244        return true;
 245    }
 246
 247    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 248    private void Guard(int column, string messageTemplate = null)
 249    {
 155894250        if ((uint) column >= (uint) Width)
 251        {
 10252            if (messageTemplate == null)
 253            {
 2254                throw new OutOfBoundsException($"Column {column} is out of bounds.");
 255            }
 256
 8257            throw new OutOfBoundsException(messageTemplate.Replace("{column}", column.ToString()));
 258        }
 155884259    }
 260}