This commit is contained in:
Christian Kauer 2023-12-19 19:03:26 +01:00
parent 44fafd4e28
commit c775c431f5
18 changed files with 530 additions and 40 deletions

View File

@ -2,6 +2,7 @@
using GameModel;
using GameModel.DeathGame;
using NuGet.Frameworks;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@ -73,11 +74,22 @@ namespace GameHandler.UnitTests.DeathGame
public void InvalidFirstThrow_RaisesExpense()
{
var gm = _gh.InitGameModel(_players, _settings);
ExpenseTrigger tr = ExpenseTrigger.Circle;
_gh.ExpenseTriggered += (trigger) => tr = trigger;
var newGm = _gh.CalcNextModel(gm, 2, false, true);
Coffin? failedCoffin = null;
_gh.FirstThrowFailed += (trigger) => failedCoffin = trigger;
var newGm = _gh.CalcNextModel(gm, 2, true, false);
Assert.That(tr, Is.EqualTo(ExpenseTrigger.FirstThrowFail));
Assert.That(failedCoffin.PlayerId, Is.EqualTo(newGm.Coffins.Last().PlayerId));
}
[Test]
public void ValidFirstThrow_DoesNotRaisesExpense()
{
var gm = _gh.InitGameModel(_players, _settings);
Coffin? failedCoffin = null;
_gh.FirstThrowFailed += (trigger) => failedCoffin = trigger;
var newGm = _gh.CalcNextModel(gm, 3, true, false);
Assert.That(failedCoffin, Is.Null);
}
@ -86,7 +98,7 @@ namespace GameHandler.UnitTests.DeathGame
{
var gm = _gh.InitGameModel(_players, _settings);
var newGm = _gh.CalcNextModel(gm, 2, false, true);
var newGm = _gh.CalcNextModel(gm, 2, true, false);
Assert.That(newGm.Coffins.Last().XCount, Is.EqualTo(1));
}
@ -105,7 +117,8 @@ namespace GameHandler.UnitTests.DeathGame
var gm = _gh.InitGameModel(_players, _settings);
var newGm = _gh.CalcNextModel(gm, 0, false, false);
Assert.That(newGm.Coffins.Last().XCount, Is.EqualTo(1));
Assert.That(newGm.Coffins.Last().LineCount, Is.EqualTo(1));
Assert.That(newGm.Coffins.Last().XCount, Is.EqualTo(0));
}
[Test]
@ -115,13 +128,13 @@ namespace GameHandler.UnitTests.DeathGame
var coffins = gm.Coffins.ToList();
var first = gm.Coffins.First() with { XCount = 2 };
coffins.RemoveAt(0);
coffins.Insert(0, first);
coffins.Insert(0, first);
var gm2 = gm with { Coffins = coffins.ToArray(), Id = 2 };
var newGm = _gh.CalcNextModel(gm2, 2, true, true);
Assert.That(newGm.Coffins.Last().XCount, Is.EqualTo(0));
Assert.That(newGm.Coffins.Last().LineCount, Is.EqualTo(first.LineCount + 1));
Assert.That(newGm.Coffins.Last().LineCount, Is.EqualTo(first.LineCount + 2)); // one for third x, one for failed throw
}
[Test]
@ -168,6 +181,7 @@ namespace GameHandler.UnitTests.DeathGame
Assert.That(deadCoffin.PlayerId, Is.EqualTo(first.PlayerId));
Assert.That(newGm.Coffins.FirstOrDefault(_ => _.PlayerId == deadCoffin.PlayerId), Is.Null);
}
[Test]
@ -185,15 +199,60 @@ namespace GameHandler.UnitTests.DeathGame
var newGm = _gh.CalcNextModel(gm2, 2, false, true);
Assert.That(deadCoffin.PlayerId, Is.EqualTo(last.PlayerId));
Assert.That(newGm.Coffins.FirstOrDefault(_ => _.PlayerId == deadCoffin.PlayerId), Is.Null);
}
[Test]
public void NexxtPlayerIsDead_WhenCurrentCausesThirdX()
public void NextPlayerIsDead_WhenCurrentCausesThirdX()
{
var gm = _gh.InitGameModel(_players, _settings);
Coffin? deadCoffin = null;
_gh.CoffinCompleted += (c) => deadCoffin = c;
var coffins = gm.Coffins.ToList();
var next = coffins[1] with { XCount = 2, LineCount = _settings.MaxCoffinSize };
coffins.RemoveAt(1);
coffins.Insert(1, next);
var gm2 = gm with { Coffins = coffins.ToArray(), Id = 2 };
var newGm = _gh.CalcNextModel(gm2, 2, false, true);
Assert.That(deadCoffin.PlayerId, Is.EqualTo(next.PlayerId));
Assert.That(newGm.Coffins.FirstOrDefault(_ => _.PlayerId == deadCoffin.PlayerId), Is.Null);
}
[Test]
public void OnePlayer_CanKillPreviousAndNext()
{
var gm = _gh.InitGameModel(_players, _settings);
var bodies = new List<Coffin>();
_gh.CoffinCompleted += (c) => bodies.Add(c);
var coffins = gm.Coffins.ToList();
var next = coffins[1] with { XCount = 2, LineCount = _settings.MaxCoffinSize };
coffins.RemoveAt(1);
coffins.Insert(1, next);
var last = coffins.Last() with { XCount = 2, LineCount = _settings.MaxCoffinSize };
coffins.RemoveAt(coffins.Count()-1);
coffins.Insert(coffins.Count(), last);
var gm2 = gm with { Coffins = coffins.ToArray(), Id = 2 };
var newGm = _gh.CalcNextModel(gm2, 2, false, true);
Assert.That(bodies.Count, Is.EqualTo(2));
foreach (var body in bodies)
{
Assert.That(newGm.Coffins.FirstOrDefault(_ => _.PlayerId == body.PlayerId), Is.Null);
}
}
}
}

View File

@ -0,0 +1,53 @@
using GameModel;
using GameModel.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameHandler.UnitTests
{
[TestFixture]
internal class PinPictureTests
{
[Test]
[TestCase(1,PinState.Down)]
[TestCase(2, PinState.Down)]
[TestCase(3, PinState.Down)]
[TestCase(4, PinState.Down)]
[TestCase(5, PinState.Down)]
[TestCase(6, PinState.Down)]
[TestCase(7, PinState.Down)]
[TestCase(8, PinState.Down)]
[TestCase(9, PinState.Down)]
public void PinPicture_IndexTest(int pinNumber, PinState pinState)
{
var p = new PinPicture();
p[pinNumber] = pinState;
Assert.That(p[pinNumber],Is.EqualTo(PinState.Down));
}
[Test]
[TestCase(0)]
[TestCase(-1)]
[TestCase(10)]
[TestCase(11)]
public void Get_Invalid_PinNumber_ThrowsException(int pinNumber)
{
var p = new PinPicture();
Assert.That(() => p[pinNumber],Throws.TypeOf<InvalidPinIndexException>());
}
[Test]
[TestCase(0)]
[TestCase(-1)]
[TestCase(10)]
[TestCase(11)]
public void Set_Invalid_PinNumber_ThrowsException(int pinNumber)
{
var p = new PinPicture();
Assert.That(() => p[pinNumber] = PinState.Down, Throws.TypeOf<InvalidPinIndexException>());
}
}
}

View File

@ -0,0 +1,64 @@
using GameModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameHandler.UnitTests
{
[TestFixture]
internal class PinThrowTests
{
[Test]
public void AllNineThrow_IsNotIsNotCircle()
{
var pic = new PinPicture(PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down);
var t = new PinThrow(pic,false,false);
Assert.That(t.IsSink, Is.False);
Assert.That(t.IsNinePins, Is.True);
Assert.That(t.IsCircle, Is.False);
Assert.That(t.IsNoWood, Is.False);
Assert.That(t.PinCount, Is.EqualTo(9));
}
[Test]
public void SinkThrow_IsNothing()
{
var pic = new PinPicture(PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Down);
var t = new PinThrow(pic, false, true);
Assert.That(t.IsSink, Is.True);
Assert.That(t.IsNinePins, Is.False);
Assert.That(t.IsCircle, Is.False);
Assert.That(t.IsNoWood, Is.False);
Assert.That(t.PinCount, Is.EqualTo(0));
}
[Test]
public void Circle_IsNotAllNine()
{
var pic = new PinPicture(PinState.Down, PinState.Down, PinState.Down, PinState.Down, PinState.Up, PinState.Down, PinState.Down, PinState.Down, PinState.Down);
var t = new PinThrow(pic, false, false);
Assert.That(t.IsSink, Is.False);
Assert.That(t.IsNinePins, Is.False);
Assert.That(t.IsCircle, Is.True);
Assert.That(t.IsNoWood, Is.False);
Assert.That(t.PinCount, Is.EqualTo(8));
}
[Test]
public void NoWood_IsNotSink()
{
var pic = new PinPicture(PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up);
var t = new PinThrow(pic, false, false);
Assert.That(t.IsSink, Is.False);
Assert.That(t.IsNinePins, Is.False);
Assert.That(t.IsCircle, Is.False);
Assert.That(t.IsNoWood, Is.True);
Assert.That(t.PinCount, Is.EqualTo(0));
}
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameHandler.UnitTests
{
public class ThrowHandlerTests
{
}
}

View File

@ -1,7 +0,0 @@
namespace GameHandler
{
public class Class1
{
}
}

View File

@ -9,7 +9,8 @@ using System.Threading.Tasks;
namespace GameHandler.DeathGame
{
public delegate void CoffinCompleted(Coffin coffin);
public delegate void ExpenseTriggered(ExpenseTrigger expenseType);
public delegate void FirstThrowFailed(Coffin coffin);
public class DeathGameHandler
{
const int MIN_PLAYER_COUNT = 3;
@ -18,7 +19,7 @@ namespace GameHandler.DeathGame
const int MAX_COFFIN_SIZE = 12;
public event CoffinCompleted CoffinCompleted;
public event ExpenseTriggered ExpenseTriggered;
public event FirstThrowFailed FirstThrowFailed;
private static Random random = new Random();
@ -66,14 +67,18 @@ namespace GameHandler.DeathGame
public DeathGameModel CalcNextModel(DeathGameModel gm, int pinCount, bool isTrowIntoAllPins, bool boardCleared)
{
var current = gm.Coffins.First();
var next = gm.Coffins.Skip(1).First();
var previous = gm.Coffins.Last();
var incLine = false;
var incX = false;
if (gm.Id == 1) // first round in game
if (isTrowIntoAllPins) // into all pins
{
incX = true;
if (pinCount < 3)
{
ExpenseTriggered?.Invoke(ExpenseTrigger.FirstThrowFail);
FirstThrowFailed?.Invoke(current);
incLine = true;
}
}
@ -88,14 +93,6 @@ namespace GameHandler.DeathGame
incLine = true;
}
var current = gm.Coffins.First();
var next = gm.Coffins.Skip(1).First();
var previous = gm.Coffins.Last();
Coffin? currentUpdated = null;
Coffin? nextUpdated = next;
Coffin? previousUpdated = previous;
var xCount = current.XCount;
var lineCount = current.LineCount;
@ -114,7 +111,10 @@ namespace GameHandler.DeathGame
xCount = 0;
lineCount++;
}
currentUpdated = current with { LineCount = lineCount, XCount = xCount };
Coffin? nextUpdated = next;
Coffin? previousUpdated = previous;
Coffin? currentUpdated = current with { LineCount = lineCount, XCount = xCount };
if (lineCount > gm.deathGameSettings.MaxCoffinSize)
{
@ -139,10 +139,10 @@ namespace GameHandler.DeathGame
}
else
{
nextUpdated = next with { LineCount = previous.LineCount + 1, XCount = 0 };
nextUpdated = next with { LineCount = next.LineCount + 1, XCount = 0 };
if (nextUpdated.LineCount > gm.deathGameSettings.MaxCoffinSize)
{
CoffinCompleted?.Invoke(nextUpdated);
CoffinCompleted?.Invoke(nextUpdated);
nextUpdated = null;
}
}

View File

@ -0,0 +1,44 @@
using GameModel;
namespace GameHandler
{
public class ThrowHandler
{
public BoardState Init(ThrowMode throwMode)
{
var picture = new PinPicture();
return new BoardState(throwMode,picture);
}
/// <summary>
/// Bild stellen
/// </summary>
/// <param name="boardstate"></param>
/// <param name="throwMode"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public BoardState SetBoardstatePicture(PinPicture boardstate, ThrowMode throwMode)
{
throw new NotImplementedException();
}
/// <summary>
/// neu autstellen
/// </summary>
/// <param name="currentState"></param>
/// <param name="pinThrow"></param>
/// <returns></returns>
public BoardState Update(BoardState currentState, PinThrow pinThrow)
{
if (currentState.ThrowMode == ThrowMode.Reposition)
{
return currentState with { PinPicture = new PinPicture() };
}
else
{
return currentState with { PinPicture = currentState.PinPicture + pinThrow };
}
}
}
}

10
GameModel/BoardState.cs Normal file
View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameModel
{
public record BoardState(ThrowMode ThrowMode, PinPicture PinPicture );
}

View File

@ -1,7 +0,0 @@
namespace GameModel
{
public class Class1
{
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameModel.Exceptions
{
public class InvalidPinIndexException : KoogleException
{
public InvalidPinIndexException() : base("PinIndex is based on one - mus be 1..9")
{
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameModel.Exceptions
{
public class InvalidPinPictureException : KoogleException
{
public InvalidPinPictureException() : base("pinpicure is not valid")
{
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameModel.Exceptions
{
public class KoogleException : Exception
{
public KoogleException(string message, Exception innerException) : base(message, innerException)
{ }
public KoogleException(string message) : base(message)
{ }
}
}

10
GameModel/Pin.cs Normal file
View File

@ -0,0 +1,10 @@
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
//namespace GameModel
//{
// public record Pin(PinState PinState);
//}

145
GameModel/PinPicture.cs Normal file
View File

@ -0,0 +1,145 @@
using GameModel.Exceptions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
namespace GameModel
{
public record PinPicture : IEnumerable<PinState>, IEnumerable
{
private readonly List<PinState> pins = new List<PinState>();
public PinPicture() : this(PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up, PinState.Up)
{
}
public PinPicture(PinState pinState1, PinState pinState2, PinState pinState3, PinState pinState4, PinState pinState5, PinState pinState6, PinState pinState7, PinState pinState8, PinState pinState9)
{
pins.Add(pinState1);
pins.Add(pinState2);
pins.Add(pinState3);
pins.Add(pinState4);
pins.Add(pinState5);
pins.Add(pinState6);
pins.Add(pinState7);
pins.Add(pinState8);
pins.Add(pinState9);
//ValidatePinPicture(pins);
}
//private PinPicture(IEnumerable<PinState> pins)
//{
// this.pins = pins.Select(_ => _).ToList();
// ValidatePinPicture(this.pins);
//}
private void ValidatePinPicture(IEnumerable<PinState> pins)
{
//if (pins.Count() != 9)
//{
// throw new InvalidPinPictureException();
//}
//if (pins.All(_ => _ == PinState.Down))
//{
// throw new InvalidPinPictureException();
//}
//if (pins.Count(_ => _ == PinState.Up) == 0)
//{
// throw new InvalidPinPictureException();
//}
//if (ThrowCounter >= ThrowsPerRound)
//{
// throw new InvalidPinPictureException();
//}
}
public PinState this[int index]
{
get
{
if (index < 1 || index > 9)
{
throw new InvalidPinIndexException();
}
return pins[index - 1];
}
set
{
if (index < 1 || index > 9)
{
throw new InvalidPinIndexException();
}
pins[index - 1] = value;
}
}
public PinState PinState1 { get { return this[1]; } set { this[1] = value; } }
public PinState PinState2 { get { return this[2]; } set { this[2] = value; } }
public PinState PinState3 { get { return this[3]; } set { this[3] = value; } }
public PinState PinState4 { get { return this[4]; } set { this[4] = value; } }
public PinState PinState5 { get { return this[5]; } set { this[5] = value; } }
public PinState PinState6 { get { return this[6]; } set { this[6] = value; } }
public PinState PinState7 { get { return this[7]; } set { this[7] = value; } }
public PinState PinState8 { get { return this[8]; } set { this[8] = value; } }
public PinState PinState9 { get { return this[9]; } set { this[9] = value; } }
public int UpCount
{
get { return pins.Count(_ => _ == PinState.Up); }
}
public int DownCount
{
get { return pins.Count(_ => _ == PinState.Down); }
}
public bool AllUp
{
get
{
// count down-pins to respect na-pins
return DownCount == 0;
}
}
public static PinPicture operator +(PinPicture pic, PinThrow pinThrow)
{
if (pinThrow.IsSink)
return pic with { };
return pic with
{
PinState1 = pinThrow.PicPicture.PinState1 == PinState.Down ? PinState.Down : pic.PinState1,
PinState2 = pinThrow.PicPicture.PinState2 == PinState.Down ? PinState.Down : pic.PinState2,
PinState3 = pinThrow.PicPicture.PinState3 == PinState.Down ? PinState.Down : pic.PinState3,
PinState4 = pinThrow.PicPicture.PinState4 == PinState.Down ? PinState.Down : pic.PinState4,
PinState5 = pinThrow.PicPicture.PinState5 == PinState.Down ? PinState.Down : pic.PinState5,
PinState6 = pinThrow.PicPicture.PinState6 == PinState.Down ? PinState.Down : pic.PinState6,
PinState7 = pinThrow.PicPicture.PinState7 == PinState.Down ? PinState.Down : pic.PinState7,
PinState8 = pinThrow.PicPicture.PinState8 == PinState.Down ? PinState.Down : pic.PinState8,
PinState9 = pinThrow.PicPicture.PinState9 == PinState.Down ? PinState.Down : pic.PinState9
};
}
public IEnumerator<PinState> GetEnumerator()
{
foreach (var pin in pins)
{
yield return pin;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}

14
GameModel/PinState.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameModel
{
public enum PinState
{
Up,
Down,
}
}

22
GameModel/PinThrow.cs Normal file
View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameModel
{
public record PinThrow(PinPicture PicPicture, bool IsBell, bool IsSink)
{
public bool IsCircle => !IsSink && PicPicture.DownCount == 8 && (PicPicture[5] == PinState.Up);
public bool IsNinePins => !IsSink && PinCount == 9;
/// <summary>
/// Count of hit pins with this throw
/// </summary>
public int PinCount => IsSink ? 0 : PicPicture.DownCount;
public bool IsNoWood => PicPicture.DownCount == 0 && !IsSink;
}
}

14
GameModel/ThrowMode.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameModel
{
public enum ThrowMode
{
Reposition, // in die Vollen
Decrease // Abräumen
}
}

10
GameModel/ThrowModel.cs Normal file
View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameModel
{
public record class ThrowModel();
}