KoogleV4/GameHandler/GameHandler/DeathGameHandler.cs

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);
}
}
}
}