mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-21 14:50:54 -08:00
115 lines
3.7 KiB
C#
115 lines
3.7 KiB
C#
using Batnum.Properties;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Batnum
|
|
{
|
|
public enum WinOptions
|
|
{
|
|
/// <summary>
|
|
/// Last person to play wins
|
|
/// </summary>
|
|
WinWithTakeLast = 1,
|
|
/// <summary>
|
|
/// Last person to play loses
|
|
/// </summary>
|
|
WinWithAvoidLast = 2
|
|
}
|
|
|
|
public enum Players
|
|
{
|
|
Computer = 1,
|
|
Human = 2
|
|
}
|
|
|
|
public class BatnumGame
|
|
{
|
|
public BatnumGame(int pileSize, WinOptions winCriteria, int minTake, int maxtake, Players firstPlayer, Func<string, int>askPlayerCallback)
|
|
{
|
|
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<string, int> askPlayerCallback;
|
|
|
|
/// <summary>
|
|
/// Returns true if the game is running
|
|
/// </summary>
|
|
public bool IsRunning => pileSize > 0;
|
|
|
|
/// <summary>
|
|
/// Takes the next turn
|
|
/// </summary>
|
|
/// <returns>A message to be displayed to the player</returns>
|
|
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);
|
|
}
|
|
}
|
|
}
|