230 lines
7.2 KiB
C#
230 lines
7.2 KiB
C#
using GameModel;
|
|
using GameModel.Contract;
|
|
using GameModel.Contracts;
|
|
using GameModel.DeathGame;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace GameHandler.DeathGame
|
|
{
|
|
public delegate void CoffinCompleted(Coffin coffin);
|
|
|
|
public class DeathGameHandler : IGameHandler
|
|
{
|
|
const int MIN_PLAYER_COUNT = 3;
|
|
const int MAX_PLAYER_COUNT = 12;
|
|
const int MIN_COFFIN_SIZE = 6;
|
|
const int MAX_COFFIN_SIZE = 12;
|
|
public const string GAMENAME_DEATHBOX = "Totenkiste";
|
|
|
|
public event CoffinCompleted CoffinCompleted;
|
|
public event AsyncEventHandler<EventArgs>? GameExpenseOccured;
|
|
|
|
private static Random random = new Random();
|
|
|
|
|
|
private void ValidatePlayerCount(int playerCount)
|
|
{
|
|
if (playerCount < MIN_PLAYER_COUNT)
|
|
{
|
|
throw new InvalidDataException($"MaxPlayerCount must be at least {MIN_PLAYER_COUNT}");
|
|
}
|
|
if (playerCount > MAX_PLAYER_COUNT)
|
|
{
|
|
throw new InvalidDataException($"MaxPlayerCount cannot be greater than {MAX_PLAYER_COUNT}");
|
|
}
|
|
}
|
|
|
|
private void ValidateGameSettings(DeathGameSettings deathGameSettings)
|
|
{
|
|
if (deathGameSettings.MaxCoffinSize < MIN_COFFIN_SIZE || deathGameSettings.MaxCoffinSize > MAX_COFFIN_SIZE )
|
|
{
|
|
throw new InvalidDataException($"Max coffin size must be from {MIN_COFFIN_SIZE} to {MAX_COFFIN_SIZE}");
|
|
}
|
|
}
|
|
|
|
public DeathGameHandler()
|
|
{
|
|
}
|
|
|
|
|
|
public IGameModel InitGameModel(int[] playerIds, IGameSettings gameSettings)
|
|
{
|
|
return InitGameModel(playerIds, gameSettings as DeathGameSettings);
|
|
}
|
|
|
|
public DeathGameModel InitGameModel(int[] playerIds, DeathGameSettings deathGameSettings)
|
|
{
|
|
ValidatePlayerCount(playerIds.Count());
|
|
ValidateGameSettings(deathGameSettings);
|
|
|
|
var coffins = new List<Coffin>();
|
|
foreach (var playerId in playerIds)
|
|
{
|
|
if (coffins.Any(_ => _.PlayerId.Equals(playerId)))
|
|
{
|
|
throw new InvalidDataException($"player {playerId} already exists");
|
|
}
|
|
coffins.Add(new Coffin(playerId,0,0));
|
|
}
|
|
return new DeathGameModel(1, coffins.OrderBy(_ => random.Next()).ToList().ToArray(), deathGameSettings, playerIds);
|
|
}
|
|
|
|
public IGameModel Update(PinThrow pinThrow, IGameModel gameModel, BoardState boardStateBeforeUpdate,
|
|
ThrowState throwStateAfterUpdate)
|
|
{
|
|
var isTrowIntoAllPins = boardStateBeforeUpdate.PinPicture.UpCount == 9;
|
|
var boardCleared = pinThrow.IsCleared;
|
|
var firstThrowInRound = throwStateAfterUpdate.ThrowCount == 1;
|
|
return CalcNextModel(gameModel as DeathGameModel, pinThrow.PinCount,
|
|
isTrowIntoAllPins, boardCleared, firstThrowInRound);
|
|
}
|
|
|
|
public DeathGameModel CalcNextModel(DeathGameModel gm, int pinCount, bool isTrowIntoAllPins,
|
|
bool boardCleared, bool firstThrowInRound)
|
|
{
|
|
var current = gm.Coffins.First();
|
|
var next = gm.Coffins.Skip(1).First();
|
|
var previous = gm.Coffins.Last();
|
|
|
|
var incLine = false;
|
|
var incX = false;
|
|
if (isTrowIntoAllPins) // into all pins
|
|
{
|
|
incX = true;
|
|
if ((pinCount < 3) && firstThrowInRound)
|
|
{
|
|
OnFirstThrowFailed(gm, current);
|
|
incLine = true;
|
|
}
|
|
}
|
|
|
|
if (isTrowIntoAllPins)
|
|
{
|
|
incX = true;
|
|
}
|
|
|
|
if (pinCount == 0)
|
|
{
|
|
incLine = true;
|
|
}
|
|
|
|
var xCount = current.XCount;
|
|
var lineCount = current.LineCount;
|
|
|
|
if (incLine)
|
|
{
|
|
lineCount++;
|
|
}
|
|
|
|
if (incX)
|
|
{
|
|
xCount++;
|
|
}
|
|
|
|
if (xCount == 3)
|
|
{
|
|
xCount = 0;
|
|
lineCount++;
|
|
}
|
|
|
|
Coffin? nextUpdated = next;
|
|
Coffin? previousUpdated = previous;
|
|
Coffin? currentUpdated = current with { LineCount = lineCount, XCount = xCount };
|
|
|
|
if (lineCount > gm.deathGameSettings.MaxCoffinSize)
|
|
{
|
|
CoffinCompleted?.Invoke(currentUpdated);
|
|
currentUpdated = null;
|
|
}
|
|
|
|
|
|
if (boardCleared)
|
|
{
|
|
previousUpdated = previous with { LineCount = previous.LineCount + 1 };
|
|
if (previousUpdated.LineCount > gm.deathGameSettings.MaxCoffinSize)
|
|
{
|
|
CoffinCompleted?.Invoke(previousUpdated);
|
|
previousUpdated = null;
|
|
}
|
|
|
|
|
|
if (next.XCount < 2)
|
|
{
|
|
nextUpdated = next with { XCount = next.XCount + 1 };
|
|
}
|
|
else
|
|
{
|
|
nextUpdated = next with { LineCount = next.LineCount + 1, XCount = 0 };
|
|
if (nextUpdated.LineCount > gm.deathGameSettings.MaxCoffinSize)
|
|
{
|
|
CoffinCompleted?.Invoke(nextUpdated);
|
|
nextUpdated = null;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
var coffins = gm.Coffins.Skip(1).ToList();
|
|
if (currentUpdated != null)
|
|
{
|
|
coffins.Add(currentUpdated);
|
|
}
|
|
|
|
if (nextUpdated == null)
|
|
{
|
|
coffins.Remove(next);
|
|
}
|
|
|
|
if (previousUpdated == null)
|
|
{
|
|
coffins.Remove(previous);
|
|
}
|
|
else
|
|
{
|
|
var idx = coffins.IndexOf(previous);
|
|
|
|
coffins.Remove(previous);
|
|
coffins.Insert(idx, previousUpdated);
|
|
}
|
|
|
|
var result = gm with { Coffins = coffins.ToArray(), Id = gm.Id + 1 };
|
|
|
|
return result;
|
|
}
|
|
|
|
private void OnFirstThrowFailed(DeathGameModel gm, Coffin current)
|
|
{
|
|
OnGameExpenseOccured(new GameExenseEventArgs(current.PlayerId, new[] { ExpenseTrigger.FirstThrowFail }, gm.PlayerIds));
|
|
}
|
|
|
|
public static string GameName()
|
|
{
|
|
return GAMENAME_DEATHBOX;
|
|
}
|
|
|
|
public ThrowMode ThrowMode() => GameModel.ThrowMode.Decrease;
|
|
|
|
public bool FreePlayerSelection() => false;
|
|
|
|
public int GetCurrentPlayerId(IGameModel gameModel)
|
|
{
|
|
var gm = gameModel as DeathGameModel;
|
|
return gm.Coffins.First().PlayerId;
|
|
}
|
|
|
|
public int ThrowsPerRount() => IGameHandler.INFINIT_THROWS;
|
|
|
|
protected async virtual Task OnGameExpenseOccured(GameExenseEventArgs e)
|
|
{
|
|
if (GameExpenseOccured is not null)
|
|
{
|
|
await GameExpenseOccured(this, e);
|
|
}
|
|
}
|
|
}
|
|
}
|