using Batnum.Properties; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Batnum { public enum WinOptions { /// /// Last person to play wins /// WinWithTakeLast = 1, /// /// Last person to play loses /// WinWithAvoidLast = 2 } public enum Players { Computer = 1, Human = 2 } public class BatnumGame { public BatnumGame(int pileSize, WinOptions winCriteria, int minTake, int maxtake, Players firstPlayer, FuncaskPlayerCallback) { this.pileSize = pileSize; this.winCriteria = winCriteria; this.minTake = minTake; this.maxTake = maxtake; this.currentPlayer = firstPlayer; this.askPlayerCallback = askPlayerCallback; } private int pileSize; private WinOptions winCriteria; private int minTake; private int maxTake; private Players currentPlayer; private Func askPlayerCallback; /// /// Returns true if the game is running /// public bool IsRunning => pileSize > 0; /// /// Takes the next turn /// /// A message to be displayed to the player public string TakeTurn() { //Edge condition - can occur when minTake is more > 1 if (pileSize < minTake) { pileSize = 0; return string.Format(Resources.END_DRAW, minTake); } return currentPlayer == Players.Computer ? ComputerTurn() : PlayerTurn(); } private string PlayerTurn() { int draw = askPlayerCallback(Resources.INPUT_TURN); if (draw == 0) { pileSize = 0; return Resources.INPUT_ZERO; } if (draw < minTake || draw > maxTake || draw > pileSize) { return Resources.INPUT_ILLEGAL; } pileSize = pileSize - draw; if (pileSize == 0) { return winCriteria == WinOptions.WinWithTakeLast ? Resources.END_PLAYERWIN : Resources.END_PLAYERLOSE; } currentPlayer = Players.Computer; return ""; } private string ComputerTurn() { //first calculate the move to play int sumTake = minTake + maxTake; int draw = pileSize - sumTake * (int)(pileSize / (float)sumTake); draw = Math.Clamp(draw, minTake, maxTake); //detect win/lose conditions switch (winCriteria) { case WinOptions.WinWithAvoidLast when (pileSize == minTake): //lose condition pileSize = 0; return string.Format(Resources.END_COMPLOSE, minTake); case WinOptions.WinWithAvoidLast when (pileSize <= maxTake): //avoid automatic loss on next turn draw = Math.Clamp(draw, minTake, pileSize - 1); break; case WinOptions.WinWithTakeLast when pileSize <= maxTake: // win condition draw = Math.Min(pileSize, maxTake); pileSize = 0; return string.Format(Resources.END_COMPWIN, draw); } pileSize -= draw; currentPlayer = Players.Human; return string.Format(Resources.COMPTURN, draw, pileSize); } } }