Yuriy Lyeshchenko
2022-01-08 19:00:19 -06:00
parent 62537a61bf
commit c8fccde889
10 changed files with 530 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
namespace Boxing;
public abstract class AttackStrategy
{
protected const int KnockoutDamageThreshold = 35;
protected readonly Boxer Other;
protected readonly Stack<Action> Work;
private readonly Action _notifyGameEnded;
public AttackStrategy(Boxer other, Stack<Action> work, Action notifyGameEnded)
{
Other = other;
Work = work;
_notifyGameEnded = notifyGameEnded;
}
public void Attack()
{
var punch = GetPunch();
if (punch.IsBestPunch)
{
Other.DamageTaken += 2;
}
Work.Push(punch.Punch switch
{
Punch.FullSwing => FullSwing,
Punch.Hook => Hook,
Punch.Uppercut => Uppercut,
_ => Jab
});
}
protected abstract AttackPunch GetPunch();
protected abstract void FullSwing();
protected abstract void Hook();
protected abstract void Uppercut();
protected abstract void Jab();
protected void RegisterKnockout(string knockoutMessage)
{
Work.Clear();
_notifyGameEnded();
Console.WriteLine(knockoutMessage);
}
protected record AttackPunch(Punch Punch, bool IsBestPunch);
}

45
15_Boxing/csharp/Boxer.cs Normal file
View File

@@ -0,0 +1,45 @@
namespace Boxing;
public class Boxer
{
private int _wins;
private string Name { get; set; } = string.Empty;
public Punch BestPunch { get; set; }
public Punch Vulnerability { get; set; }
public void SetName(string prompt)
{
Console.WriteLine(prompt);
string? name;
do
{
name = Console.ReadLine();
} while (string.IsNullOrWhiteSpace(name));
Name = name;
}
public int DamageTaken { get; set; }
public void ResetForNewRound() => DamageTaken = 0;
public void RecordWin() => _wins += 1;
public bool IsWinner => _wins >= 2;
public override string ToString() => Name;
}
public class Opponent : Boxer
{
public void SetRandomPunches()
{
do
{
BestPunch = (Punch) GameUtils.Roll(4); // B1
Vulnerability = (Punch) GameUtils.Roll(4); // D1
} while (BestPunch == Vulnerability);
}
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Boxing", "Boxing.csproj", "{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,115 @@
using static Boxing.GameUtils;
using static System.Console;
namespace Boxing;
public class OpponentAttackStrategy : AttackStrategy
{
private readonly Opponent _opponent;
public OpponentAttackStrategy(Opponent opponent, Boxer player, Action notifyGameEnded, Stack<Action> work) : base(player, work, notifyGameEnded)
{
_opponent = opponent;
}
protected override AttackPunch GetPunch()
{
var punch = (Punch)Roll(4);
return new AttackPunch(punch, punch == _opponent.BestPunch);
}
protected override void FullSwing() // 720
{
Write($"{_opponent} TAKES A FULL SWING AND");
if (Other.Vulnerability == Punch.FullSwing)
{
ScoreFullSwing();
}
else
{
if (RollSatisfies(60, x => x < 30))
{
WriteLine(" IT'S BLOCKED!");
}
else
{
ScoreFullSwing();
}
}
void ScoreFullSwing()
{
WriteLine(" POW!!!!! HE HITS HIM RIGHT IN THE FACE!");
if (Other.DamageTaken > KnockoutDamageThreshold)
{
Work.Push(RegisterOtherKnockedOut);
}
Other.DamageTaken += 15;
}
}
protected override void Hook() // 810
{
Write($"{_opponent} GETS {Other} IN THE JAW (OUCH!)");
Other.DamageTaken += 7;
WriteLine("....AND AGAIN!");
Other.DamageTaken += 5;
if (Other.DamageTaken > KnockoutDamageThreshold)
{
Work.Push(RegisterOtherKnockedOut);
}
}
protected override void Uppercut() // 860
{
Write($"{Other} IS ATTACKED BY AN UPPERCUT (OH,OH)...");
if (Other.Vulnerability == Punch.Uppercut)
{
ScoreUppercut();
}
else
{
if (RollSatisfies(200, x => x > 75))
{
WriteLine($" BLOCKS AND HITS {_opponent} WITH A HOOK.");
_opponent.DamageTaken += 5;
}
else
{
ScoreUppercut();
}
}
void ScoreUppercut()
{
WriteLine($"AND {_opponent} CONNECTS...");
Other.DamageTaken += 8;
}
}
protected override void Jab() // 640
{
Write($"{_opponent} JABS AND ");
if (Other.Vulnerability == Punch.Jab)
{
ScoreJab();
}
else
{
if (RollSatisfies(7, x => x > 4))
{
WriteLine("BLOOD SPILLS !!!");
ScoreJab();
}
else
{
WriteLine("IT'S BLOCKED!");
}
}
void ScoreJab() => Other.DamageTaken += 5;
}
private void RegisterOtherKnockedOut()
=> RegisterKnockout($"{Other} IS KNOCKED COLD AND {_opponent} IS THE WINNER AND CHAMP!");
}

View File

@@ -0,0 +1,121 @@
using static Boxing.GameUtils;
using static System.Console;
namespace Boxing;
public class PlayerAttackStrategy : AttackStrategy
{
private readonly Boxer _player;
public PlayerAttackStrategy(Boxer player, Opponent opponent, Action notifyGameEnded, Stack<Action> work)
: base(opponent, work, notifyGameEnded) => _player = player;
protected override AttackPunch GetPunch()
{
var punch = GameUtils.GetPunch($"{_player}'S PUNCH");
return new AttackPunch(punch, punch == _player.BestPunch);
}
protected override void FullSwing() // 340
{
Write($"{_player} SWINGS AND ");
if (Other.Vulnerability == Punch.FullSwing)
{
ScoreFullSwing();
}
else
{
if (RollSatisfies(30, x => x < 10))
{
ScoreFullSwing();
}
else
{
WriteLine("HE MISSES");
}
}
void ScoreFullSwing()
{
WriteLine("HE CONNECTS!");
if (Other.DamageTaken > KnockoutDamageThreshold)
{
Work.Push(() => RegisterKnockout($"{Other} IS KNOCKED COLD AND {_player} IS THE WINNER AND CHAMP!"));
}
Other.DamageTaken += 15;
}
}
protected override void Uppercut() // 520
{
Write($"{_player} TRIES AN UPPERCUT ");
if (Other.Vulnerability == Punch.Uppercut)
{
ScoreUpperCut();
}
else
{
if (RollSatisfies(100, x => x < 51))
{
ScoreUpperCut();
}
else
{
WriteLine("AND IT'S BLOCKED (LUCKY BLOCK!)");
}
}
void ScoreUpperCut()
{
WriteLine("AND HE CONNECTS!");
Other.DamageTaken += 4;
}
}
protected override void Hook() // 450
{
Write($"{_player} GIVES THE HOOK... ");
if (Other.Vulnerability == Punch.Hook)
{
ScoreHookOnOpponent();
}
else
{
if (RollSatisfies(2, x => x == 1))
{
WriteLine("BUT IT'S BLOCKED!!!!!!!!!!!!!");
}
else
{
ScoreHookOnOpponent();
}
}
void ScoreHookOnOpponent()
{
WriteLine("CONNECTS...");
Other.DamageTaken += 7;
}
}
protected override void Jab()
{
WriteLine($"{_player} JABS AT {Other}'S HEAD");
if (Other.Vulnerability == Punch.Jab)
{
ScoreJabOnOpponent();
}
else
{
if (RollSatisfies(8, x => x < 4))
{
WriteLine("IT'S BLOCKED.");
}
else
{
ScoreJabOnOpponent();
}
}
void ScoreJabOnOpponent() => Other.DamageTaken += 3;
}
}

View File

@@ -0,0 +1,29 @@
using Boxing;
using static Boxing.GameUtils;
using static System.Console;
WriteLine(new string('\t', 33) + "BOXING");
WriteLine(new string('\t', 15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
WriteLine("{0}{0}{0}BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS){0}", Environment.NewLine);
var opponent = new Opponent();
opponent.SetName("WHAT IS YOUR OPPONENT'S NAME"); // J$
var player = new Boxer();
player.SetName("INPUT YOUR MAN'S NAME"); // L$
PrintPunchDescription();
player.BestPunch = GetPunch("WHAT IS YOUR MANS BEST"); // B
player.Vulnerability = GetPunch("WHAT IS HIS VULNERABILITY"); // D
opponent.SetRandomPunches();
WriteLine($"{opponent}'S ADVANTAGE IS {opponent.BestPunch.ToFriendlyString()} AND VULNERABILITY IS SECRET.");
for (var i = 1; i <= 3; i ++) // R
{
var round = new Round(player, opponent, i);
round.Start();
round.CheckOpponentWin();
round.CheckPlayerWin();
if (round.GameEnded) break;
}
WriteLine("{0}{0}AND NOW GOODBYE FROM THE OLYMPIC ARENA.{0}", Environment.NewLine);

View File

@@ -0,0 +1,9 @@
namespace Boxing;
public enum Punch
{
FullSwing = 1,
Hook = 2,
Uppercut = 3,
Jab = 4
}

96
15_Boxing/csharp/Round.cs Normal file
View File

@@ -0,0 +1,96 @@
namespace Boxing;
class Round
{
private readonly Boxer _player;
private readonly Boxer _opponent;
private readonly int _round;
private Stack<Action> _work = new();
private readonly PlayerAttackStrategy _playerAttackStrategy;
private readonly OpponentAttackStrategy _opponentAttackStrategy;
public bool GameEnded { get; private set; }
public Round(Boxer player, Opponent opponent, int round)
{
_player = player;
_opponent = opponent;
_round = round;
_work.Push(ResetPlayers);
_work.Push(CheckOpponentWin);
_work.Push(CheckPlayerWin);
void NotifyGameEnded() => GameEnded = true;
_playerAttackStrategy = new PlayerAttackStrategy(player, opponent, NotifyGameEnded, _work);
_opponentAttackStrategy = new OpponentAttackStrategy(opponent, player, NotifyGameEnded, _work);
}
public void Start()
{
while (_work.Count > 0)
{
var action = _work.Pop();
// This delay does not exist in the VB code but it makes a bit easier to follow the game.
// I assume the computers at the time were slow enough
// so that they did not need this delay...
Thread.Sleep(300);
action();
}
}
public void CheckOpponentWin()
{
if (_opponent.IsWinner)
{
Console.WriteLine($"{_opponent} WINS (NICE GOING, {_opponent}).");
GameEnded = true;
}
}
public void CheckPlayerWin()
{
if (_player.IsWinner)
{
Console.WriteLine($"{_player} AMAZINGLY WINS!!");
GameEnded = true;
}
}
private void ResetPlayers()
{
_player.ResetForNewRound();
_opponent.ResetForNewRound();
_work.Push(RoundBegins);
}
private void RoundBegins()
{
Console.WriteLine();
Console.WriteLine($"ROUND {_round} BEGINS...");
_work.Push(CheckRoundWinner);
for (var i = 0; i < 7; i++)
{
_work.Push(DecideWhoAttacks);
}
}
private void CheckRoundWinner()
{
if (_opponent.DamageTaken > _player.DamageTaken)
{
Console.WriteLine($"{_player} WINS ROUND {_round}");
_player.RecordWin();
}
else
{
Console.WriteLine($"{_opponent} WINS ROUND {_round}");
_opponent.RecordWin();
}
}
private void DecideWhoAttacks()
{
_work.Push( GameUtils.RollSatisfies(10, x => x > 5) ? _opponentAttackStrategy.Attack : _playerAttackStrategy.Attack );
}
}

35
15_Boxing/csharp/Utils.cs Normal file
View File

@@ -0,0 +1,35 @@
namespace Boxing;
public static class GameUtils
{
private static readonly Random Rnd = new((int) DateTime.UtcNow.Ticks);
public static void PrintPunchDescription() =>
Console.WriteLine($"DIFFERENT PUNCHES ARE: {PunchDesc(Punch.FullSwing)}; {PunchDesc(Punch.Hook)}; {PunchDesc(Punch.Uppercut)}; {PunchDesc(Punch.Jab)}.");
private static string PunchDesc(Punch punch) => $"({(int)punch}) {punch.ToFriendlyString()}";
public static Punch GetPunch(string prompt)
{
Console.WriteLine(prompt);
Punch result;
while (!Enum.TryParse(Console.ReadLine(), out result) || !Enum.IsDefined(typeof(Punch), result))
{
PrintPunchDescription();
}
return result;
}
public static Func<int, int> Roll { get; } = upperLimit => (int) (upperLimit * Rnd.NextSingle()) + 1;
public static bool RollSatisfies(int upperLimit, Predicate<int> predicate) => predicate(Roll(upperLimit));
public static string ToFriendlyString(this Punch punch)
=> punch switch
{
Punch.FullSwing => "FULL SWING",
Punch.Hook => "HOOK",
Punch.Uppercut => "UPPERCUT",
Punch.Jab => "JAB",
_ => throw new ArgumentOutOfRangeException(nameof(punch), punch, null)
};
}