mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-29 14:15:08 -08:00
25
17 Bullfight/csharp/Bullfight.sln
Normal file
25
17 Bullfight/csharp/Bullfight.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31321.278
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Game", "Game.csproj", "{8F7C450E-5F3A-45BA-9DB9-329744214931}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8F7C450E-5F3A-45BA-9DB9-329744214931}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8F7C450E-5F3A-45BA-9DB9-329744214931}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8F7C450E-5F3A-45BA-9DB9-329744214931}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8F7C450E-5F3A-45BA-9DB9-329744214931}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C5BFC749-C7D8-4981-A7D4-1D401901A890}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
7
17 Bullfight/csharp/Game.csproj
Normal file
7
17 Bullfight/csharp/Game.csproj
Normal file
@@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
24
17 Bullfight/csharp/src/Action.cs
Normal file
24
17 Bullfight/csharp/src/Action.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different actions that the player can take on each round
|
||||
/// of the fight.
|
||||
/// </summary>
|
||||
public enum Action
|
||||
{
|
||||
/// <summary>
|
||||
/// Dodge the bull.
|
||||
/// </summary>
|
||||
Dodge,
|
||||
|
||||
/// <summary>
|
||||
/// Kill the bull.
|
||||
/// </summary>
|
||||
Kill,
|
||||
|
||||
/// <summary>
|
||||
/// Freeze in place and don't do anything.
|
||||
/// </summary>
|
||||
Panic
|
||||
}
|
||||
}
|
||||
44
17 Bullfight/csharp/src/ActionResult.cs
Normal file
44
17 Bullfight/csharp/src/ActionResult.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different possible outcomes of the player's action.
|
||||
/// </summary>
|
||||
public enum ActionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The fight continues.
|
||||
/// </summary>
|
||||
FightContinues,
|
||||
|
||||
/// <summary>
|
||||
/// The player fled from the ring.
|
||||
/// </summary>
|
||||
PlayerFlees,
|
||||
|
||||
/// <summary>
|
||||
/// The bull has gored the player.
|
||||
/// </summary>
|
||||
BullGoresPlayer,
|
||||
|
||||
/// <summary>
|
||||
/// The bull killed the player.
|
||||
/// </summary>
|
||||
BullKillsPlayer,
|
||||
|
||||
/// <summary>
|
||||
/// The player killed the bull.
|
||||
/// </summary>
|
||||
PlayerKillsBull,
|
||||
|
||||
/// <summary>
|
||||
/// The player attempted to kill the bull and both survived.
|
||||
/// </summary>
|
||||
Draw
|
||||
}
|
||||
}
|
||||
220
17 Bullfight/csharp/src/BullFight.cs
Normal file
220
17 Bullfight/csharp/src/BullFight.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a method for simulating a bull fight.
|
||||
/// </summary>
|
||||
public static class BullFight
|
||||
{
|
||||
/// <summary>
|
||||
/// Begins a new fight.
|
||||
/// </summary>
|
||||
/// <param name="mediator">
|
||||
/// Object used to communicate with the player.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The sequence of events that take place during the fight.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// After receiving each event, the caller must invoke the appropriate
|
||||
/// mediator method to inform this coroutine what to do next. Failure
|
||||
/// to do so will result in an exception.
|
||||
/// </remarks>
|
||||
public static IEnumerable<Events.Event> Begin(Mediator mediator)
|
||||
{
|
||||
var random = new Random();
|
||||
var result = ActionResult.FightContinues;
|
||||
|
||||
var bullQuality = GetBullQuality();
|
||||
var toreadorePerformance = GetHelpQuality(bullQuality);
|
||||
var picadorePerformance = GetHelpQuality(bullQuality);
|
||||
|
||||
var bullStrength = 6 - (int)bullQuality;
|
||||
var assistanceLevel = (12 - (int)toreadorePerformance - (int)picadorePerformance) * 0.1;
|
||||
var bravery = 1.0;
|
||||
var style = 1.0;
|
||||
var passNumber = 0;
|
||||
|
||||
yield return new Events.MatchStarted(
|
||||
bullQuality,
|
||||
toreadorePerformance,
|
||||
picadorePerformance,
|
||||
GetHumanCasualties(toreadorePerformance),
|
||||
GetHumanCasualties(picadorePerformance),
|
||||
GetHorseCasualties(picadorePerformance));
|
||||
|
||||
while (result == ActionResult.FightContinues)
|
||||
{
|
||||
yield return new Events.BullCharging(++passNumber);
|
||||
|
||||
var (action, riskLevel) = mediator.GetInput<(Action, RiskLevel)>();
|
||||
result = action switch
|
||||
{
|
||||
Action.Dodge => TryDodge(riskLevel),
|
||||
Action.Kill => TryKill(riskLevel),
|
||||
_ => Panic()
|
||||
};
|
||||
|
||||
var first = true;
|
||||
while (result == ActionResult.BullGoresPlayer)
|
||||
{
|
||||
yield return new Events.PlayerGored(action == Action.Panic, first);
|
||||
first = false;
|
||||
|
||||
result = TrySurvive();
|
||||
if (result == ActionResult.FightContinues)
|
||||
{
|
||||
yield return new Events.PlayerSurvived();
|
||||
|
||||
var runFromRing = mediator.GetInput<bool>();
|
||||
if (runFromRing)
|
||||
result = Flee();
|
||||
else
|
||||
result = IgnoreInjury(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield return new Events.MatchCompleted(
|
||||
result,
|
||||
bravery == 2,
|
||||
GetReward());
|
||||
|
||||
Quality GetBullQuality() =>
|
||||
(Quality)random.Next(1, 6);
|
||||
|
||||
Quality GetHelpQuality(Quality bullQuality) =>
|
||||
((3.0 / (int)bullQuality) * random.NextDouble()) switch
|
||||
{
|
||||
< 0.37 => Quality.Superb,
|
||||
< 0.50 => Quality.Good,
|
||||
< 0.63 => Quality.Fair,
|
||||
< 0.87 => Quality.Poor,
|
||||
_ => Quality.Awful
|
||||
};
|
||||
|
||||
int GetHumanCasualties(Quality performance) =>
|
||||
performance switch
|
||||
{
|
||||
Quality.Poor => random.Next(0, 2),
|
||||
Quality.Awful => random.Next(1, 3),
|
||||
_ => 0
|
||||
};
|
||||
|
||||
int GetHorseCasualties(Quality performance) =>
|
||||
performance switch
|
||||
{
|
||||
// NOTE: The code for displaying a single horse casuality
|
||||
// following a poor picadore peformance was unreachable
|
||||
// in the original BASIC version. I've assumed this was
|
||||
// a bug.
|
||||
Quality.Poor => 1,
|
||||
Quality.Awful => random.Next(1, 3),
|
||||
_ => 0
|
||||
};
|
||||
|
||||
ActionResult TryDodge(RiskLevel riskLevel)
|
||||
{
|
||||
var difficultyModifier = riskLevel switch
|
||||
{
|
||||
RiskLevel.High => 3.0,
|
||||
RiskLevel.Medium => 2.0,
|
||||
_ => 0.5
|
||||
};
|
||||
|
||||
var outcome = (bullStrength + (difficultyModifier / 10)) * random.NextDouble() /
|
||||
((assistanceLevel + (passNumber / 10.0)) * 5);
|
||||
|
||||
if (outcome < 0.51)
|
||||
{
|
||||
style += difficultyModifier;
|
||||
return ActionResult.FightContinues;
|
||||
}
|
||||
else
|
||||
return ActionResult.BullGoresPlayer;
|
||||
}
|
||||
|
||||
ActionResult TryKill(RiskLevel riskLevel)
|
||||
{
|
||||
var luck = bullStrength * 10 * random.NextDouble() / (assistanceLevel * 5 * passNumber);
|
||||
|
||||
return ((riskLevel == RiskLevel.High && luck > 0.2) || luck > 0.8) ?
|
||||
ActionResult.BullGoresPlayer : ActionResult.PlayerKillsBull;
|
||||
}
|
||||
|
||||
ActionResult Panic() =>
|
||||
ActionResult.BullGoresPlayer;
|
||||
|
||||
ActionResult TrySurvive()
|
||||
{
|
||||
if (random.Next(2) == 0)
|
||||
{
|
||||
bravery = 1.5;
|
||||
return ActionResult.BullKillsPlayer;
|
||||
}
|
||||
else
|
||||
return ActionResult.FightContinues;
|
||||
}
|
||||
|
||||
ActionResult Flee()
|
||||
{
|
||||
bravery = 0.0;
|
||||
return ActionResult.PlayerFlees;
|
||||
}
|
||||
|
||||
ActionResult IgnoreInjury(Action action)
|
||||
{
|
||||
if (random.Next(2) == 0)
|
||||
{
|
||||
bravery = 2.0;
|
||||
return action == Action.Dodge ? ActionResult.FightContinues : ActionResult.Draw;
|
||||
}
|
||||
else
|
||||
return ActionResult.BullGoresPlayer;
|
||||
}
|
||||
|
||||
Reward GetReward()
|
||||
{
|
||||
var score = CalculateScore();
|
||||
|
||||
if (score * random.NextDouble() < 2.4)
|
||||
return Reward.Nothing;
|
||||
else
|
||||
if (score * random.NextDouble() < 4.9)
|
||||
return Reward.OneEar;
|
||||
else
|
||||
if (score * random.NextDouble() < 7.4)
|
||||
return Reward.TwoEars;
|
||||
else
|
||||
return Reward.CarriedFromRing;
|
||||
}
|
||||
|
||||
double CalculateScore()
|
||||
{
|
||||
var score = 4.5;
|
||||
|
||||
// Style
|
||||
score += style / 6;
|
||||
|
||||
// Assisstance
|
||||
score -= assistanceLevel * 2.5;
|
||||
|
||||
// Courage
|
||||
score += 4 * bravery;
|
||||
|
||||
// Kill bonus
|
||||
score += (result == ActionResult.PlayerKillsBull) ? 4 : 2;
|
||||
|
||||
// Match length
|
||||
score -= Math.Pow(passNumber, 2) / 120;
|
||||
|
||||
// Difficulty
|
||||
score -= (int)bullQuality;
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
17 Bullfight/csharp/src/Controller.cs
Normal file
134
17 Bullfight/csharp/src/Controller.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains functions for getting input from the user.
|
||||
/// </summary>
|
||||
public static class Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the initial interaction with the player.
|
||||
/// </summary>
|
||||
public static void StartGame()
|
||||
{
|
||||
View.ShowBanner();
|
||||
View.PromptShowInstructions();
|
||||
|
||||
var input = Console.ReadLine();
|
||||
if (input is null)
|
||||
Environment.Exit(0);
|
||||
|
||||
if (input.ToUpperInvariant() != "NO")
|
||||
View.ShowInstructions();
|
||||
|
||||
View.ShowSeparator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player's action for the current round.
|
||||
/// </summary>
|
||||
/// <param name="passNumber">
|
||||
/// The current pass number.
|
||||
/// </param>
|
||||
public static (Action action, RiskLevel riskLevel) GetPlayerIntention(int passNumber)
|
||||
{
|
||||
if (passNumber < 3)
|
||||
View.PromptKillBull();
|
||||
else
|
||||
View.PromptKillBullBrief();
|
||||
|
||||
var attemptToKill = GetYesOrNo();
|
||||
|
||||
if (attemptToKill)
|
||||
{
|
||||
View.PromptKillMethod();
|
||||
|
||||
var input = Console.ReadLine();
|
||||
if (input is null)
|
||||
Environment.Exit(0);
|
||||
|
||||
return input switch
|
||||
{
|
||||
"4" => (Action.Kill, RiskLevel.High),
|
||||
"5" => (Action.Kill, RiskLevel.Low),
|
||||
_ => (Action.Panic, default(RiskLevel))
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (passNumber < 2)
|
||||
View.PromptCapeMove();
|
||||
else
|
||||
View.PromptCapeMoveBrief();
|
||||
|
||||
var action = Action.Panic;
|
||||
var riskLevel = default(RiskLevel);
|
||||
|
||||
while (action == Action.Panic)
|
||||
{
|
||||
var input = Console.ReadLine();
|
||||
if (input is null)
|
||||
Environment.Exit(0);
|
||||
|
||||
(action, riskLevel) = input switch
|
||||
{
|
||||
"0" => (Action.Dodge, RiskLevel.High),
|
||||
"1" => (Action.Dodge, RiskLevel.Medium),
|
||||
"2" => (Action.Dodge, RiskLevel.Low),
|
||||
_ => (Action.Panic, default(RiskLevel))
|
||||
};
|
||||
|
||||
if (action == Action.Panic)
|
||||
View.PromptDontPanic();
|
||||
}
|
||||
|
||||
return (action, riskLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player's intention to flee (or not).
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True if the player flees; otherwise, false.
|
||||
/// </returns>
|
||||
public static bool GetPlayerRunsFromRing()
|
||||
{
|
||||
View.PromptRunFromRing();
|
||||
|
||||
var playerFlees = GetYesOrNo();
|
||||
if (!playerFlees)
|
||||
View.ShowPlayerFoolhardy();
|
||||
|
||||
return playerFlees;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a yes or no response from the player.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True if the user answered yes; otherwise, false.
|
||||
/// </returns>
|
||||
public static bool GetYesOrNo()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var input = Console.ReadLine();
|
||||
if (input is null)
|
||||
Environment.Exit(0);
|
||||
|
||||
switch (input.ToUpperInvariant())
|
||||
{
|
||||
case "YES":
|
||||
return true;
|
||||
case "NO":
|
||||
return false;
|
||||
default:
|
||||
Console.WriteLine("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
17 Bullfight/csharp/src/Events/BullCharging.cs
Normal file
7
17 Bullfight/csharp/src/Events/BullCharging.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Game.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the bull is charing the player.
|
||||
/// </summary>
|
||||
public sealed record BullCharging(int PassNumber) : Event;
|
||||
}
|
||||
7
17 Bullfight/csharp/src/Events/Event.cs
Normal file
7
17 Bullfight/csharp/src/Events/Event.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Game.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Common base class for all events in the game.
|
||||
/// </summary>
|
||||
public abstract record Event();
|
||||
}
|
||||
7
17 Bullfight/csharp/src/Events/MatchCompleted.cs
Normal file
7
17 Bullfight/csharp/src/Events/MatchCompleted.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Game.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the fight has completed.
|
||||
/// </summary>
|
||||
public sealed record MatchCompleted(ActionResult Result, bool ExtremeBravery, Reward Reward) : Event;
|
||||
}
|
||||
13
17 Bullfight/csharp/src/Events/MatchStarted.cs
Normal file
13
17 Bullfight/csharp/src/Events/MatchStarted.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Game.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that a new match has started.
|
||||
/// </summary>
|
||||
public sealed record MatchStarted(
|
||||
Quality BullQuality,
|
||||
Quality ToreadorePerformance,
|
||||
Quality PicadorePerformance,
|
||||
int ToreadoresKilled,
|
||||
int PicadoresKilled,
|
||||
int HorsesKilled) : Event;
|
||||
}
|
||||
7
17 Bullfight/csharp/src/Events/PlayerGored.cs
Normal file
7
17 Bullfight/csharp/src/Events/PlayerGored.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Game.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the player has been gored by the bull.
|
||||
/// </summary>
|
||||
public sealed record PlayerGored(bool Panicked, bool FirstGoring) : Event;
|
||||
}
|
||||
7
17 Bullfight/csharp/src/Events/PlayerSurvived.cs
Normal file
7
17 Bullfight/csharp/src/Events/PlayerSurvived.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Game.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the player has survived being gored by the bull.
|
||||
/// </summary>
|
||||
public sealed record PlayerSurvived() : Event;
|
||||
}
|
||||
48
17 Bullfight/csharp/src/Mediator.cs
Normal file
48
17 Bullfight/csharp/src/Mediator.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Facilitates sending messages between the two game loops.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class serves as a little piece of glue in between the main program
|
||||
/// loop and the bull fight coroutine. When the main program calls one of
|
||||
/// its methods, the mediator creates the appropriate input data that the
|
||||
/// bull fight coroutine later retrieves with <see cref="GetInput{T}"/>.
|
||||
/// </remarks>
|
||||
public class Mediator
|
||||
{
|
||||
private object? m_input;
|
||||
|
||||
public void Dodge(RiskLevel riskLevel) =>
|
||||
m_input = (Action.Dodge, riskLevel);
|
||||
|
||||
public void Kill(RiskLevel riskLevel) =>
|
||||
m_input = (Action.Kill, riskLevel);
|
||||
|
||||
public void Panic() =>
|
||||
m_input = (Action.Panic, default(RiskLevel));
|
||||
|
||||
public void RunFromRing() =>
|
||||
m_input = true;
|
||||
|
||||
public void ContinueFighting() =>
|
||||
m_input = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next input from the user.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The type of input to receive.
|
||||
/// </typeparam>
|
||||
public T GetInput<T>()
|
||||
{
|
||||
Debug.Assert(m_input is not null, "No input received");
|
||||
Debug.Assert(m_input.GetType() == typeof(T), "Invalid input received");
|
||||
var result = (T)m_input;
|
||||
m_input = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
17 Bullfight/csharp/src/Program.cs
Normal file
54
17 Bullfight/csharp/src/Program.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace Game
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main()
|
||||
{
|
||||
Controller.StartGame();
|
||||
|
||||
var mediator = new Mediator();
|
||||
foreach (var evt in BullFight.Begin(mediator))
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case Events.MatchStarted matchStarted:
|
||||
View.ShowStartingConditions(matchStarted);
|
||||
break;
|
||||
|
||||
case Events.BullCharging bullCharging:
|
||||
View.ShowStartOfPass(bullCharging.PassNumber);
|
||||
var (action, riskLevel) = Controller.GetPlayerIntention(bullCharging.PassNumber);
|
||||
switch (action)
|
||||
{
|
||||
case Action.Dodge:
|
||||
mediator.Dodge(riskLevel);
|
||||
break;
|
||||
case Action.Kill:
|
||||
mediator.Kill(riskLevel);
|
||||
break;
|
||||
case Action.Panic:
|
||||
mediator.Panic();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Events.PlayerGored playerGored:
|
||||
View.ShowPlayerGored(playerGored.Panicked, playerGored.FirstGoring);
|
||||
break;
|
||||
|
||||
case Events.PlayerSurvived:
|
||||
View.ShowPlayerSurvives();
|
||||
if (Controller.GetPlayerRunsFromRing())
|
||||
mediator.RunFromRing();
|
||||
else
|
||||
mediator.ContinueFighting();
|
||||
break;
|
||||
|
||||
case Events.MatchCompleted matchCompleted:
|
||||
View.ShowFinalResult(matchCompleted.Result, matchCompleted.ExtremeBravery, matchCompleted.Reward);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
17 Bullfight/csharp/src/Quality.cs
Normal file
19
17 Bullfight/csharp/src/Quality.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different levels of quality in the game.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Quality applies both to the bull and to the help received from the
|
||||
/// toreadores and picadores. Note that the ordinal values are significant
|
||||
/// (these are used in various calculations).
|
||||
/// </remarks>
|
||||
public enum Quality
|
||||
{
|
||||
Superb = 1,
|
||||
Good = 2,
|
||||
Fair = 3,
|
||||
Poor = 4,
|
||||
Awful = 5
|
||||
}
|
||||
}
|
||||
13
17 Bullfight/csharp/src/Reward.cs
Normal file
13
17 Bullfight/csharp/src/Reward.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different things the player can be awarded.
|
||||
/// </summary>
|
||||
public enum Reward
|
||||
{
|
||||
Nothing,
|
||||
OneEar,
|
||||
TwoEars,
|
||||
CarriedFromRing
|
||||
}
|
||||
}
|
||||
12
17 Bullfight/csharp/src/RiskLevel.cs
Normal file
12
17 Bullfight/csharp/src/RiskLevel.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different levels of risk for manoeuvres in the game.
|
||||
/// </summary>
|
||||
public enum RiskLevel
|
||||
{
|
||||
Low,
|
||||
Medium,
|
||||
High
|
||||
}
|
||||
}
|
||||
242
17 Bullfight/csharp/src/View.cs
Normal file
242
17 Bullfight/csharp/src/View.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using System;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains functions for displaying information to the user.
|
||||
/// </summary>
|
||||
public static class View
|
||||
{
|
||||
private static readonly string[] QualityString = { "SUPERB", "GOOD", "FAIR", "POOR", "AWFUL" };
|
||||
|
||||
public static void ShowBanner()
|
||||
{
|
||||
Console.WriteLine(" BULL");
|
||||
Console.WriteLine(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
public static void ShowInstructions()
|
||||
{
|
||||
Console.WriteLine("HELLO, ALL YOU BLOODLOVERS AND AFICIONADOS.");
|
||||
Console.WriteLine("HERE IS YOUR BIG CHANCE TO KILL A BULL.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("ON EACH PASS OF THE BULL, YOU MAY TRY");
|
||||
Console.WriteLine("0 - VERONICA (DANGEROUS INSIDE MOVE OF THE CAPE)");
|
||||
Console.WriteLine("1 - LESS DANGEROUS OUTSIDE MOVE OF THE CAPE");
|
||||
Console.WriteLine("2 - ORDINARY SWIRL OF THE CAPE.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("INSTEAD OF THE ABOVE, YOU MAY TRY TO KILL THE BULL");
|
||||
Console.WriteLine("ON ANY TURN: 4 (OVER THE HORNS), 5 (IN THE CHEST).");
|
||||
Console.WriteLine("BUT IF I WERE YOU,");
|
||||
Console.WriteLine("I WOULDN'T TRY IT BEFORE THE SEVENTH PASS.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("THE CROWD WILL DETERMINE WHAT AWARD YOU DESERVE");
|
||||
Console.WriteLine("(POSTHUMOUSLY IF NECESSARY).");
|
||||
Console.WriteLine("THE BRAVER YOU ARE, THE BETTER THE AWARD YOU RECEIVE.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("THE BETTER THE JOB THE PICADORES AND TOREADORES DO,");
|
||||
Console.WriteLine("THE BETTER YOUR CHANCES ARE.");
|
||||
}
|
||||
|
||||
public static void ShowSeparator()
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
public static void ShowStartingConditions(Events.MatchStarted matchStarted)
|
||||
{
|
||||
ShowBullQuality();
|
||||
ShowHelpQuality("TOREADORES", matchStarted.ToreadorePerformance, matchStarted.ToreadoresKilled, 0);
|
||||
ShowHelpQuality("PICADORES", matchStarted.PicadorePerformance, matchStarted.PicadoresKilled, matchStarted.HorsesKilled);
|
||||
|
||||
void ShowBullQuality()
|
||||
{
|
||||
Console.WriteLine($"YOU HAVE DRAWN A {QualityString[(int)matchStarted.BullQuality - 1]} BULL.");
|
||||
|
||||
if (matchStarted.BullQuality > Quality.Poor)
|
||||
{
|
||||
Console.WriteLine("YOU'RE LUCKY");
|
||||
}
|
||||
else
|
||||
if (matchStarted.BullQuality < Quality.Good)
|
||||
{
|
||||
Console.WriteLine("GOOD LUCK. YOU'LL NEED IT.");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static void ShowHelpQuality(string helperName, Quality helpQuality, int helpersKilled, int horsesKilled)
|
||||
{
|
||||
Console.WriteLine($"THE {helperName} DID A {QualityString[(int)helpQuality - 1]} JOB.");
|
||||
|
||||
// NOTE: The code below makes some *strong* assumptions about
|
||||
// how the casualty numbers were generated. It is written
|
||||
// this way to preserve the behaviour of the original BASIC
|
||||
// version, but it would make more sense ignore the helpQuality
|
||||
// parameter and just use the provided numbers to decide what
|
||||
// to display.
|
||||
switch (helpQuality)
|
||||
{
|
||||
case Quality.Poor:
|
||||
if (horsesKilled > 0)
|
||||
Console.WriteLine($"ONE OF THE HORSES OF THE {helperName} WAS KILLED.");
|
||||
|
||||
if (helpersKilled > 0)
|
||||
Console.WriteLine($"ONE OF THE {helperName} WAS KILLED.");
|
||||
else
|
||||
Console.WriteLine($"NO {helperName} WERE KILLED.");
|
||||
break;
|
||||
|
||||
case Quality.Awful:
|
||||
if (horsesKilled > 0)
|
||||
Console.WriteLine($" {horsesKilled} OF THE HORSES OF THE {helperName} KILLED.");
|
||||
|
||||
Console.WriteLine($" {helpersKilled} OF THE {helperName} KILLED.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowStartOfPass(int passNumber)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"PASS NUMBER {passNumber}");
|
||||
}
|
||||
|
||||
public static void ShowPlayerGored(bool playerPanicked, bool firstGoring)
|
||||
{
|
||||
Console.WriteLine((playerPanicked, firstGoring) switch
|
||||
{
|
||||
(true, true) => "YOU PANICKED. THE BULL GORED YOU.",
|
||||
(false, true) => "THE BULL HAS GORED YOU!",
|
||||
(_, false) => "YOU ARE GORED AGAIN!"
|
||||
});
|
||||
}
|
||||
|
||||
public static void ShowPlayerSurvives()
|
||||
{
|
||||
Console.WriteLine("YOU ARE STILL ALIVE.");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
public static void ShowPlayerFoolhardy()
|
||||
{
|
||||
Console.WriteLine("YOU ARE BRAVE. STUPID, BUT BRAVE.");
|
||||
}
|
||||
|
||||
public static void ShowFinalResult(ActionResult result, bool extremeBravery, Reward reward)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case ActionResult.PlayerFlees:
|
||||
Console.WriteLine("COWARD");
|
||||
break;
|
||||
case ActionResult.BullKillsPlayer:
|
||||
Console.WriteLine("YOU ARE DEAD.");
|
||||
break;
|
||||
case ActionResult.PlayerKillsBull:
|
||||
Console.WriteLine("YOU KILLED THE BULL!");
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == ActionResult.PlayerFlees)
|
||||
{
|
||||
Console.WriteLine("THE CROWD BOOS FOR TEN MINUTES. IF YOU EVER DARE TO SHOW");
|
||||
Console.WriteLine("YOUR FACE IN A RING AGAIN, THEY SWEAR THEY WILL KILL YOU--");
|
||||
Console.WriteLine("UNLESS THE BULL DOES FIRST.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (extremeBravery)
|
||||
Console.WriteLine("THE CROWD CHEERS WILDLY!");
|
||||
else
|
||||
if (result == ActionResult.PlayerKillsBull)
|
||||
{
|
||||
Console.WriteLine("THE CROWD CHEERS!");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
Console.WriteLine("THE CROWD AWARDS YOU");
|
||||
switch (reward)
|
||||
{
|
||||
case Reward.Nothing:
|
||||
Console.WriteLine("NOTHING AT ALL.");
|
||||
break;
|
||||
case Reward.OneEar:
|
||||
Console.WriteLine("ONE EAR OF THE BULL.");
|
||||
break;
|
||||
case Reward.TwoEars:
|
||||
Console.WriteLine("BOTH EARS OF THE BULL!");
|
||||
Console.WriteLine("OLE!");
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine("OLE! YOU ARE 'MUY HOMBRE'!! OLE! OLE!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("ADIOS");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
public static void PromptShowInstructions()
|
||||
{
|
||||
Console.Write("DO YOU WANT INSTRUCTIONS? ");
|
||||
}
|
||||
|
||||
public static void PromptKillBull()
|
||||
{
|
||||
Console.WriteLine("THE BULL IS CHARGING AT YOU! YOU ARE THE MATADOR--");
|
||||
Console.Write("DO YOU WANT TO KILL THE BULL? ");
|
||||
}
|
||||
|
||||
public static void PromptKillBullBrief()
|
||||
{
|
||||
Console.Write("HERE COMES THE BULL. TRY FOR A KILL? ");
|
||||
}
|
||||
|
||||
public static void PromptKillMethod()
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("IT IS THE MOMENT OF TRUTH.");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.Write("HOW DO YOU TRY TO KILL THE BULL? ");
|
||||
}
|
||||
|
||||
public static void PromptCapeMove()
|
||||
{
|
||||
Console.Write("WHAT MOVE DO YOU MAKE WITH THE CAPE? ");
|
||||
}
|
||||
|
||||
public static void PromptCapeMoveBrief()
|
||||
{
|
||||
Console.Write("CAPE MOVE? ");
|
||||
}
|
||||
|
||||
public static void PromptDontPanic()
|
||||
{
|
||||
Console.WriteLine("DON'T PANIC, YOU IDIOT! PUT DOWN A CORRECT NUMBER");
|
||||
Console.Write("? ");
|
||||
}
|
||||
|
||||
public static void PromptRunFromRing()
|
||||
{
|
||||
Console.Write("DO YOU RUN FROM THE RING? ");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user