diff --git a/53_King/README.md b/53_King/README.md index a363a2be..14a66fe9 100644 --- a/53_King/README.md +++ b/53_King/README.md @@ -1,10 +1,10 @@ ## King -This is one of the most comprehensive, difficult, and interesting games. (If you’ve never played one of these games, start with HAMMURABI.) +This is one of the most comprehensive, difficult, and interesting games. (If you've never played one of these games, start with HAMMURABI.) In this game, you are Premier of Setats Detinu, a small communist island 30 by 70 miles long. Your job is to decide upon the budget of your country and distribute money to your country from the communal treasury. -The money system is Rollods; each person needs 100 Rallods per year to survive. Your country’s income comes from farm produce and tourists visiting your magnificent forests, hunting, fishing, etc. Part of your land is farm land but it also has an excellent mineral content and may be sold to foreign industry for strip mining. Industry import and support their own workers. Crops cost between 10 and 15 Rallods per square mile to plant, cultivate, and harvest. Your goal is to complete an eight-year term of office without major mishap. A word of warning: it isn’t easy! +The money system is Rollods; each person needs 100 Rallods per year to survive. Your country's income comes from farm produce and tourists visiting your magnificent forests, hunting, fishing, etc. Part of your land is farm land but it also has an excellent mineral content and may be sold to foreign industry for strip mining. Industry import and support their own workers. Crops cost between 10 and 15 Rallods per square mile to plant, cultivate, and harvest. Your goal is to complete an eight-year term of office without major mishap. A word of warning: it isn't easy! The author of this program is James A. Storer who wrote it while a student at Lexington High School. @@ -66,3 +66,15 @@ On basic line 1997 it is: but it should be: 1997 PRINT " AND 1,000 SQ. MILES OF FOREST LAND." + +### Bug 4 + +On basic line 1310 we see this: + + 1310 IF C=0 THEN 1324 + 1320 PRINT "OF ";INT(J);"SQ. MILES PLANTED,"; + 1324 ... + +but it should probably be: + + 1310 IF J=0 THEN 1324 diff --git a/53_King/csharp/Country.cs b/53_King/csharp/Country.cs new file mode 100644 index 00000000..1a37bcd8 --- /dev/null +++ b/53_King/csharp/Country.cs @@ -0,0 +1,133 @@ +namespace King; + +internal class Country +{ + private const int InitialLand = 1000; + + private readonly IReadWrite _io; + private readonly IRandom _random; + private float _rallods; + private float _countrymen; + private float _foreigners; + private float _arableLand; + private float _industryLand; + + public Country(IReadWrite io, IRandom random) + : this( + io, + random, + (int)(60000 + random.NextFloat(1000) - random.NextFloat(1000)), + (int)(500 + random.NextFloat(10) - random.NextFloat(10)), + 0, + InitialLand) + { + } + + public Country(IReadWrite io, IRandom random, float rallods, float countrymen, float foreigners, float land) + { + _io = io; + _random = random; + _rallods = rallods; + _countrymen = countrymen; + _foreigners = foreigners; + _arableLand = land; + } + + public string GetStatus(int landValue, int plantingCost) + => Resource.Status(_rallods, _countrymen, _foreigners, _arableLand, landValue, plantingCost); + + public float Countrymen => _countrymen; + public float Workers => _foreigners; + public bool HasWorkers => _foreigners > 0; + private float FarmLand => _arableLand; + public bool HasRallods => _rallods > 0; + public float Rallods => _rallods; + public float IndustryLand => InitialLand - _arableLand; + public int PreviousTourismIncome { get; private set; } + + public bool SellLand(int landValue, out float landSold) + { + if (_io.TryReadValue( + SellLandPrompt, + out landSold, + new ValidityTest(v => v <= FarmLand, () => SellLandError(FarmLand)))) + { + _arableLand = (int)(_arableLand - landSold); + _rallods = (int)(_rallods + landSold * landValue); + return true; + } + + return false; + } + + public bool DistributeRallods(out float rallodsGiven) + { + if (_io.TryReadValue( + GiveRallodsPrompt, + out rallodsGiven, + new ValidityTest(v => v <= _rallods, () => GiveRallodsError(_rallods)))) + { + _rallods = (int)(_rallods - rallodsGiven); + return true; + } + + return false; + } + + public bool PlantLand(int plantingCost, out float landPlanted) + { + if (_io.TryReadValue( + PlantLandPrompt, + out landPlanted, + new ValidityTest(v => v <= _countrymen * 2, PlantLandError1), + new ValidityTest(v => v <= FarmLand, PlantLandError2(FarmLand)), + new ValidityTest(v => v * plantingCost <= _rallods, PlantLandError3(_rallods)))) + { + _rallods -= (int)(landPlanted * plantingCost); + return true; + } + + return false; + } + + public bool ControlPollution(out float rallodsSpent) + { + if (_io.TryReadValue( + PollutionPrompt, + out rallodsSpent, + new ValidityTest(v => v <= _rallods, () => PollutionError(_rallods)))) + { + _rallods = (int)(_rallods - rallodsSpent); + return true; + } + + return false; + } + + public bool TrySpend(float amount, float landValue) + { + if (_rallods >= amount) + { + _rallods -= amount; + return true; + } + + _arableLand = (int)(_arableLand - (int)(amount - _rallods) / landValue); + _rallods = 0; + return false; + } + + public void RemoveTheDead(int deaths) => _countrymen = (int)(_countrymen - deaths); + + public void Migration(int migration) => _countrymen = (int)(_countrymen + migration); + + public void AddWorkers(int newWorkers) => _foreigners = (int)(_foreigners + newWorkers); + + public void SellCrops(int income) => _rallods = (int)(_rallods + income); + + public void EntertainTourists(int income) + { + PreviousTourismIncome = income; + _rallods = (int)(_rallods + income); + } +} diff --git a/53_King/csharp/Game.cs b/53_King/csharp/Game.cs new file mode 100644 index 00000000..6a91010c --- /dev/null +++ b/53_King/csharp/Game.cs @@ -0,0 +1,47 @@ +namespace King; + +internal class Game +{ + const int TermOfOffice = 8; + + private readonly IReadWrite _io; + private readonly IRandom _random; + + public Game(IReadWrite io, IRandom random) + { + _io = io; + _random = random; + } + + public void Play() + { + _io.Write(Title); + + var reign = SetUpReign(); + if (reign != null) + { + while (reign.PlayYear()); + } + + _io.WriteLine(); + _io.WriteLine(); + } + + private Reign? SetUpReign() + { + var response = _io.ReadString(InstructionsPrompt).ToUpper(); + + if (response.Equals("Again", StringComparison.InvariantCultureIgnoreCase)) + { + return _io.TryReadGameData(_random, out var reign) ? reign : null; + } + + if (!response.StartsWith("N", StringComparison.InvariantCultureIgnoreCase)) + { + _io.Write(InstructionsText(TermOfOffice)); + } + + _io.WriteLine(); + return new Reign(_io, _random); + } +} diff --git a/53_King/csharp/IOExtensions.cs b/53_King/csharp/IOExtensions.cs new file mode 100644 index 00000000..dbb3079a --- /dev/null +++ b/53_King/csharp/IOExtensions.cs @@ -0,0 +1,61 @@ +using System.Diagnostics.CodeAnalysis; +using static King.Resources.Resource; + +namespace King; + +internal static class IOExtensions +{ + internal static bool TryReadGameData(this IReadWrite io, IRandom random, [NotNullWhen(true)] out Reign? reign) + { + if (io.TryReadValue(SavedYearsPrompt, v => v < Reign.MaxTerm, SavedYearsError(Reign.MaxTerm), out var years) && + io.TryReadValue(SavedTreasuryPrompt, out var rallods) && + io.TryReadValue(SavedCountrymenPrompt, out var countrymen) && + io.TryReadValue(SavedWorkersPrompt, out var workers) && + io.TryReadValue(SavedLandPrompt, v => v is > 1000 and <= 2000, SavedLandError, out var land)) + { + reign = new Reign(io, random, new Country(io, random, rallods, countrymen, workers, land), years + 1); + return true; + } + + reign = default; + return false; + } + + internal static bool TryReadValue(this IReadWrite io, string prompt, out float value, params ValidityTest[] tests) + { + while (true) + { + var response = value = io.ReadNumber(prompt); + if (response == 0) { return false; } + if (tests.All(test => test.IsValid(response, io))) { return true; } + } + } + + internal static bool TryReadValue(this IReadWrite io, string prompt, out float value) + => io.TryReadValue(prompt, _ => true, "", out value); + + internal static bool TryReadValue( + this IReadWrite io, + string prompt, + Predicate isValid, + string error, + out float value) + => io.TryReadValue(prompt, isValid, () => error, out value); + + internal static bool TryReadValue( + this IReadWrite io, + string prompt, + Predicate isValid, + Func getError, + out float value) + { + while (true) + { + value = io.ReadNumber(prompt); + if (value < 0) { return false; } + if (isValid(value)) { return true; } + + io.Write(getError()); + } + } +} diff --git a/53_King/csharp/King.csproj b/53_King/csharp/King.csproj index d3fe4757..3870320c 100644 --- a/53_King/csharp/King.csproj +++ b/53_King/csharp/King.csproj @@ -6,4 +6,12 @@ enable enable + + + + + + + + diff --git a/53_King/csharp/Program.cs b/53_King/csharp/Program.cs new file mode 100644 index 00000000..5aae4ccb --- /dev/null +++ b/53_King/csharp/Program.cs @@ -0,0 +1,7 @@ +global using Games.Common.IO; +global using Games.Common.Randomness; +global using King.Resources; +global using static King.Resources.Resource; +using King; + +new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); diff --git a/53_King/csharp/Reign.cs b/53_King/csharp/Reign.cs new file mode 100644 index 00000000..7a96a30f --- /dev/null +++ b/53_King/csharp/Reign.cs @@ -0,0 +1,45 @@ +namespace King; + +internal class Reign +{ + public const int MaxTerm = 8; + + private readonly IReadWrite _io; + private readonly IRandom _random; + private readonly Country _country; + private float _yearNumber; + + public Reign(IReadWrite io, IRandom random) + : this(io, random, new Country(io, random), 1) + { + } + + public Reign(IReadWrite io, IRandom random, Country country, float year) + { + _io = io; + _random = random; + _country = country; + _yearNumber = year; + } + + public bool PlayYear() + { + var year = new Year(_country, _random, _io); + + _io.Write(year.Status); + + var result = year.GetPlayerActions() ?? year.EvaluateResults() ?? IsAtEndOfTerm(); + if (result.IsGameOver) + { + _io.WriteLine(result.Message); + return false; + } + + return true; + } + + private Result IsAtEndOfTerm() + => _yearNumber == MaxTerm + ? Result.GameOver(EndCongratulations(MaxTerm)) + : Result.Continue; +} diff --git a/53_King/csharp/Resources/DeathsPollution.txt b/53_King/csharp/Resources/DeathsPollution.txt new file mode 100644 index 00000000..bf28d9a7 --- /dev/null +++ b/53_King/csharp/Resources/DeathsPollution.txt @@ -0,0 +1 @@ + {0} countrymen died of carbon-monoxide and dust inhalation \ No newline at end of file diff --git a/53_King/csharp/Resources/DeathsStarvation.txt b/53_King/csharp/Resources/DeathsStarvation.txt new file mode 100644 index 00000000..af275cf4 --- /dev/null +++ b/53_King/csharp/Resources/DeathsStarvation.txt @@ -0,0 +1 @@ + {0} countrymen died of starvation \ No newline at end of file diff --git a/53_King/csharp/Resources/Emigration.txt b/53_King/csharp/Resources/Emigration.txt new file mode 100644 index 00000000..01f67094 --- /dev/null +++ b/53_King/csharp/Resources/Emigration.txt @@ -0,0 +1 @@ + {0} countrymen left the island. \ No newline at end of file diff --git a/53_King/csharp/Resources/EndAlso.txt b/53_King/csharp/Resources/EndAlso.txt new file mode 100644 index 00000000..084b9a82 --- /dev/null +++ b/53_King/csharp/Resources/EndAlso.txt @@ -0,0 +1,3 @@ +also had your left eye gouged out! +;have also gained a very bad reputation. +;have also been declared national fink. diff --git a/53_King/csharp/Resources/EndCongratulations.txt b/53_King/csharp/Resources/EndCongratulations.txt new file mode 100644 index 00000000..ef1ff203 --- /dev/null +++ b/53_King/csharp/Resources/EndCongratulations.txt @@ -0,0 +1,10 @@ + + +Congratulations!!!!!!!!!!!!!!!!!! +You have successfully completed your {0} year term +of office. You were, of course, extremely lucky, but +nevertheless, it's quite an achievement. Goodbye and good +luck - you'll probably need it if you're the type that +plays this game. + + diff --git a/53_King/csharp/Resources/EndConsequences.txt b/53_King/csharp/Resources/EndConsequences.txt new file mode 100644 index 00000000..61ebfd25 --- /dev/null +++ b/53_King/csharp/Resources/EndConsequences.txt @@ -0,0 +1,3 @@ +You have been thrown out of office and are now +residing in prison.; +You have been assassinated. diff --git a/53_King/csharp/Resources/EndForeignWorkers.txt b/53_King/csharp/Resources/EndForeignWorkers.txt new file mode 100644 index 00000000..76c04c3d --- /dev/null +++ b/53_King/csharp/Resources/EndForeignWorkers.txt @@ -0,0 +1,7 @@ + + +The number of foreign workers has exceeded the number +of countrymen. As a minority, they have revolted and +taken over the country. +{0} + diff --git a/53_King/csharp/Resources/EndManyDead.txt b/53_King/csharp/Resources/EndManyDead.txt new file mode 100644 index 00000000..70cd95f0 --- /dev/null +++ b/53_King/csharp/Resources/EndManyDead.txt @@ -0,0 +1,5 @@ +{0} countrymen died in one year!!!!! +due to this extreme mismanagement, you have not only +been impeached and thrown out of office, but you +{1} + diff --git a/53_King/csharp/Resources/EndMoneyLeftOver.txt b/53_King/csharp/Resources/EndMoneyLeftOver.txt new file mode 100644 index 00000000..e194a9fd --- /dev/null +++ b/53_King/csharp/Resources/EndMoneyLeftOver.txt @@ -0,0 +1,7 @@ + +Money was left over in the treasury which you did +not spend. As a result, some of your countrymen died +of starvation. The public is enraged and you have +been forced to resign. + + diff --git a/53_King/csharp/Resources/EndOneThirdDead.txt b/53_King/csharp/Resources/EndOneThirdDead.txt new file mode 100644 index 00000000..e1761dfe --- /dev/null +++ b/53_King/csharp/Resources/EndOneThirdDead.txt @@ -0,0 +1,7 @@ + + +Over one third of the population has died since you +were elected to office. The people (remaining) +hate your guts. +{0} + diff --git a/53_King/csharp/Resources/FuneralExpenses.txt b/53_King/csharp/Resources/FuneralExpenses.txt new file mode 100644 index 00000000..3aff58eb --- /dev/null +++ b/53_King/csharp/Resources/FuneralExpenses.txt @@ -0,0 +1 @@ + You were forced to spend {0} rallods on funeral expenses \ No newline at end of file diff --git a/53_King/csharp/Resources/GiveRallodsError.txt b/53_King/csharp/Resources/GiveRallodsError.txt new file mode 100644 index 00000000..df0d7cc0 --- /dev/null +++ b/53_King/csharp/Resources/GiveRallodsError.txt @@ -0,0 +1 @@ + Think again. You've only got {0} rallods in the treasury. diff --git a/53_King/csharp/Resources/GiveRallodsPrompt.txt b/53_King/csharp/Resources/GiveRallodsPrompt.txt new file mode 100644 index 00000000..e8bd345b --- /dev/null +++ b/53_King/csharp/Resources/GiveRallodsPrompt.txt @@ -0,0 +1 @@ +How many rallods will you distribute among your countrymen \ No newline at end of file diff --git a/53_King/csharp/Resources/Goodbye.txt b/53_King/csharp/Resources/Goodbye.txt new file mode 100644 index 00000000..67543c04 --- /dev/null +++ b/53_King/csharp/Resources/Goodbye.txt @@ -0,0 +1,4 @@ +Goodbye. +(If you wish to continue this game at a later date, answer +'again' when asked if you want instructions at the start +of the game). diff --git a/53_King/csharp/Resources/Harvest.txt b/53_King/csharp/Resources/Harvest.txt new file mode 100644 index 00000000..287e0c42 --- /dev/null +++ b/53_King/csharp/Resources/Harvest.txt @@ -0,0 +1,2 @@ + you harvested {0} sq. miles of crops. +{1}making {2} rallods. diff --git a/53_King/csharp/Resources/HarvestReason.txt b/53_King/csharp/Resources/HarvestReason.txt new file mode 100644 index 00000000..82faccb5 --- /dev/null +++ b/53_King/csharp/Resources/HarvestReason.txt @@ -0,0 +1 @@ + (Due to increased air and water pollution from foreign industry.) diff --git a/53_King/csharp/Resources/Immigration.txt b/53_King/csharp/Resources/Immigration.txt new file mode 100644 index 00000000..30d8b6b3 --- /dev/null +++ b/53_King/csharp/Resources/Immigration.txt @@ -0,0 +1 @@ + {0} countrymen came to the island. \ No newline at end of file diff --git a/53_King/csharp/Resources/InstructionsPrompt.txt b/53_King/csharp/Resources/InstructionsPrompt.txt new file mode 100644 index 00000000..0d311b60 --- /dev/null +++ b/53_King/csharp/Resources/InstructionsPrompt.txt @@ -0,0 +1 @@ +Do you want instructions \ No newline at end of file diff --git a/53_King/csharp/Resources/InstructionsText.txt b/53_King/csharp/Resources/InstructionsText.txt new file mode 100644 index 00000000..c576123b --- /dev/null +++ b/53_King/csharp/Resources/InstructionsText.txt @@ -0,0 +1,17 @@ + + + +Congratulations! You've just been elected Premier of Setats +Detinu, a small communist island 30 by 70 miles long. Your +job is to decide upon the country's budget and distribute +money to your countrymen from the communal treasury. +The money system is rallods, and each person needs 100 +rallods per year to survive. Your country's income comes +from farm produce and tourists visiting your magnificent +forests, hunting, fishing, etc. Half your land if farm land +which also has an excellent mineral content and may be sold +to foreign industry (strip mining) who import and support +their own workers. Crops cost between 10 and 15 rallods per +square mile to plant. +Your goal is to complete your {0} year term of office. +Good luck! diff --git a/53_King/csharp/Resources/InsufficientReserves.txt b/53_King/csharp/Resources/InsufficientReserves.txt new file mode 100644 index 00000000..e69de29b diff --git a/53_King/csharp/Resources/LandPlanted.txt b/53_King/csharp/Resources/LandPlanted.txt new file mode 100644 index 00000000..e52529aa --- /dev/null +++ b/53_King/csharp/Resources/LandPlanted.txt @@ -0,0 +1 @@ +Of {0} sq. miles planted, \ No newline at end of file diff --git a/53_King/csharp/Resources/PlantLandError1.txt b/53_King/csharp/Resources/PlantLandError1.txt new file mode 100644 index 00000000..ef959f9f --- /dev/null +++ b/53_King/csharp/Resources/PlantLandError1.txt @@ -0,0 +1 @@ + Sorry, but each countryman can only plant 2 sq. miles. \ No newline at end of file diff --git a/53_King/csharp/Resources/PlantLandError2.txt b/53_King/csharp/Resources/PlantLandError2.txt new file mode 100644 index 00000000..e5844aa1 --- /dev/null +++ b/53_King/csharp/Resources/PlantLandError2.txt @@ -0,0 +1 @@ + Sorry, but you've only {0} sq. miles of farm land. \ No newline at end of file diff --git a/53_King/csharp/Resources/PlantLandError3.txt b/53_King/csharp/Resources/PlantLandError3.txt new file mode 100644 index 00000000..38264442 --- /dev/null +++ b/53_King/csharp/Resources/PlantLandError3.txt @@ -0,0 +1 @@ + Think again, You've only {0} rallods left in the treasury. diff --git a/53_King/csharp/Resources/PlantLandPrompt.txt b/53_King/csharp/Resources/PlantLandPrompt.txt new file mode 100644 index 00000000..f2fe448e --- /dev/null +++ b/53_King/csharp/Resources/PlantLandPrompt.txt @@ -0,0 +1 @@ +How many square miles do you wish to plant \ No newline at end of file diff --git a/53_King/csharp/Resources/PollutionError.txt b/53_King/csharp/Resources/PollutionError.txt new file mode 100644 index 00000000..f99644e3 --- /dev/null +++ b/53_King/csharp/Resources/PollutionError.txt @@ -0,0 +1 @@ + Think again. You only have {0} rallods remaining. diff --git a/53_King/csharp/Resources/PollutionPrompt.txt b/53_King/csharp/Resources/PollutionPrompt.txt new file mode 100644 index 00000000..252fb333 --- /dev/null +++ b/53_King/csharp/Resources/PollutionPrompt.txt @@ -0,0 +1 @@ +How many rallods do you wish to spend on pollution control \ No newline at end of file diff --git a/53_King/csharp/Resources/Resource.cs b/53_King/csharp/Resources/Resource.cs new file mode 100644 index 00000000..5ea09e1c --- /dev/null +++ b/53_King/csharp/Resources/Resource.cs @@ -0,0 +1,112 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace King.Resources; + +internal static class Resource +{ + private static bool _sellLandErrorShown; + + public static Stream Title => GetStream(); + + public static string InstructionsPrompt => GetString(); + public static string InstructionsText(int years) => string.Format(GetString(), years); + + public static string Status( + float rallods, + float countrymen, + float workers, + float land, + float landValue, + float plantingCost) + => string.Format( + workers == 0 ? StatusWithWorkers : StatusSansWorkers, + rallods, + (int)countrymen, + (int)workers, + (int)land, + landValue, + plantingCost); + + private static string StatusWithWorkers => GetString(); + private static string StatusSansWorkers => GetString(); + + public static string SellLandPrompt => GetString(); + public static string SellLandError(float farmLand) + { + var error = string.Format(GetString(), farmLand, _sellLandErrorShown ? "" : SellLandErrorReason); + _sellLandErrorShown = true; + return error; + } + private static string SellLandErrorReason => GetString(); + + public static string GiveRallodsPrompt => GetString(); + public static string GiveRallodsError(float rallods) => string.Format(GetString(), rallods); + + public static string PlantLandPrompt => GetString(); + public static string PlantLandError1 => GetString(); + public static string PlantLandError2(float farmLand) => string.Format(GetString(), farmLand); + public static string PlantLandError3(float rallods) => string.Format(GetString(), rallods); + + public static string PollutionPrompt => GetString(); + public static string PollutionError(float rallods) => string.Format(GetString(), rallods); + + public static string DeathsStarvation(float deaths) => string.Format(GetString(), (int)deaths); + public static string DeathsPollution(int deaths) => string.Format(GetString(), deaths); + public static string FuneralExpenses(int expenses) => string.Format(GetString(), expenses); + public static string InsufficientReserves => GetString(); + + public static string WorkerMigration(int newWorkers) => string.Format(GetString(), newWorkers); + public static string Migration(int migration) + => string.Format(migration < 0 ? Emigration : Immigration, Math.Abs(migration)); + public static string Emigration => GetString(); + public static string Immigration => GetString(); + + public static string LandPlanted(float landPlanted) + => landPlanted > 0 ? string.Format(GetString(), (int)landPlanted) : ""; + public static string Harvest(int yield, int income, bool hasIndustry) + => string.Format(GetString(), yield, HarvestReason(hasIndustry), income); + private static string HarvestReason(bool hasIndustry) => hasIndustry ? GetString() : ""; + + public static string TourismEarnings(int income) => string.Format(GetString(), income); + public static string TourismDecrease(IRandom random) => string.Format(GetString(), TourismReason(random)); + private static string TourismReason(IRandom random) => GetStrings()[random.Next(5)]; + + private static string EndAlso(IRandom random) + => random.Next(10) switch + { + <= 3 => GetStrings()[0], + <= 6 => GetStrings()[1], + _ => GetStrings()[2] + }; + + public static string EndCongratulations(int termLength) => string.Format(GetString(), termLength); + private static string EndConsequences(IRandom random) => GetStrings()[random.Next(2)]; + public static string EndForeignWorkers(IRandom random) => string.Format(GetString(), EndConsequences(random)); + public static string EndManyDead(int deaths, IRandom random) => string.Format(GetString(), deaths, EndAlso(random)); + public static string EndMoneyLeftOver() => GetString(); + public static string EndOneThirdDead(IRandom random) => string.Format(GetString(), EndConsequences(random)); + + public static string SavedYearsPrompt => GetString(); + public static string SavedYearsError(int years) => string.Format(GetString(), years); + public static string SavedTreasuryPrompt => GetString(); + public static string SavedCountrymenPrompt => GetString(); + public static string SavedWorkersPrompt => GetString(); + public static string SavedLandPrompt => GetString(); + public static string SavedLandError => GetString(); + + public static string Goodbye => GetString(); + + private static string[] GetStrings([CallerMemberName] string? name = null) => GetString(name).Split(';'); + + private static string GetString([CallerMemberName] string? name = null) + { + using var stream = GetStream(name); + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + private static Stream GetStream([CallerMemberName] string? name = null) => + Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt") + ?? throw new Exception($"Could not find embedded resource stream '{name}'."); +} \ No newline at end of file diff --git a/53_King/csharp/Resources/SavedCountrymenPrompt.txt b/53_King/csharp/Resources/SavedCountrymenPrompt.txt new file mode 100644 index 00000000..120718d2 --- /dev/null +++ b/53_King/csharp/Resources/SavedCountrymenPrompt.txt @@ -0,0 +1 @@ +How many countrymen \ No newline at end of file diff --git a/53_King/csharp/Resources/SavedLandError.txt b/53_King/csharp/Resources/SavedLandError.txt new file mode 100644 index 00000000..9ac66dad --- /dev/null +++ b/53_King/csharp/Resources/SavedLandError.txt @@ -0,0 +1,2 @@ + Come on, you started with 1000 sq. miles of farm land + and 10,000 sq. miles of forest land diff --git a/53_King/csharp/Resources/SavedLandPrompt.txt b/53_King/csharp/Resources/SavedLandPrompt.txt new file mode 100644 index 00000000..e8afdf72 --- /dev/null +++ b/53_King/csharp/Resources/SavedLandPrompt.txt @@ -0,0 +1 @@ +How many square miles of land \ No newline at end of file diff --git a/53_King/csharp/Resources/SavedTreasuryPrompt.txt b/53_King/csharp/Resources/SavedTreasuryPrompt.txt new file mode 100644 index 00000000..f4a50f58 --- /dev/null +++ b/53_King/csharp/Resources/SavedTreasuryPrompt.txt @@ -0,0 +1 @@ +How much did you have in the treasury \ No newline at end of file diff --git a/53_King/csharp/Resources/SavedWorkersPrompt.txt b/53_King/csharp/Resources/SavedWorkersPrompt.txt new file mode 100644 index 00000000..b88a9c10 --- /dev/null +++ b/53_King/csharp/Resources/SavedWorkersPrompt.txt @@ -0,0 +1 @@ +How many workers \ No newline at end of file diff --git a/53_King/csharp/Resources/SavedYearsError.txt b/53_King/csharp/Resources/SavedYearsError.txt new file mode 100644 index 00000000..1c8c3c99 --- /dev/null +++ b/53_King/csharp/Resources/SavedYearsError.txt @@ -0,0 +1 @@ + Come on, your term in office is only {0} years. diff --git a/53_King/csharp/Resources/SavedYearsPrompt.txt b/53_King/csharp/Resources/SavedYearsPrompt.txt new file mode 100644 index 00000000..afbda229 --- /dev/null +++ b/53_King/csharp/Resources/SavedYearsPrompt.txt @@ -0,0 +1 @@ +How many years had you been in office when interrupted \ No newline at end of file diff --git a/53_King/csharp/Resources/SellLandError.txt b/53_King/csharp/Resources/SellLandError.txt new file mode 100644 index 00000000..83e0266c --- /dev/null +++ b/53_King/csharp/Resources/SellLandError.txt @@ -0,0 +1,2 @@ +*** Think again. You only have {0} square miles of farm land. +{1} \ No newline at end of file diff --git a/53_King/csharp/Resources/SellLandErrorReason.txt b/53_King/csharp/Resources/SellLandErrorReason.txt new file mode 100644 index 00000000..836352bb --- /dev/null +++ b/53_King/csharp/Resources/SellLandErrorReason.txt @@ -0,0 +1,4 @@ + +(Foreign industry will only buy farm land because +forest land is uneconomical to strip mine due to trees, +thicker top soil, etc.) diff --git a/53_King/csharp/Resources/SellLandPrompt.txt b/53_King/csharp/Resources/SellLandPrompt.txt new file mode 100644 index 00000000..8d01c2c2 --- /dev/null +++ b/53_King/csharp/Resources/SellLandPrompt.txt @@ -0,0 +1 @@ +How many square miles do you wish to sell to industry \ No newline at end of file diff --git a/53_King/csharp/Resources/StatusSansWorkers.txt b/53_King/csharp/Resources/StatusSansWorkers.txt new file mode 100644 index 00000000..7efb84e7 --- /dev/null +++ b/53_King/csharp/Resources/StatusSansWorkers.txt @@ -0,0 +1,6 @@ + +You now have {0} rallods in the treasury. + {1} countrymen, and {3} sq. miles of land. +This year industry will buy land for {4} rallods per square mile. +Land currently costs {5} rallods per square mile to plant. + diff --git a/53_King/csharp/Resources/StatusWithWorkers.txt b/53_King/csharp/Resources/StatusWithWorkers.txt new file mode 100644 index 00000000..e7bc5c4a --- /dev/null +++ b/53_King/csharp/Resources/StatusWithWorkers.txt @@ -0,0 +1,6 @@ + +You now have {0} rallods in the treasury. + {1} countrymen, {2} foreign workers and {3} sq. miles of land. +This year industry will buy land for {4} rallods per square mile. +Land currently costs {5} rallods per square mile to plant. + diff --git a/53_King/csharp/Resources/Title.txt b/53_King/csharp/Resources/Title.txt new file mode 100644 index 00000000..f9f7854d --- /dev/null +++ b/53_King/csharp/Resources/Title.txt @@ -0,0 +1,5 @@ + King + Creative Computing Morristown, New Jersey + + + diff --git a/53_King/csharp/Resources/TourismDecrease.txt b/53_King/csharp/Resources/TourismDecrease.txt new file mode 100644 index 00000000..dcb45fc7 --- /dev/null +++ b/53_King/csharp/Resources/TourismDecrease.txt @@ -0,0 +1 @@ + Decrease because {0} \ No newline at end of file diff --git a/53_King/csharp/Resources/TourismEarnings.txt b/53_King/csharp/Resources/TourismEarnings.txt new file mode 100644 index 00000000..db9251dd --- /dev/null +++ b/53_King/csharp/Resources/TourismEarnings.txt @@ -0,0 +1 @@ + You made {0} rallods from tourist trade. \ No newline at end of file diff --git a/53_King/csharp/Resources/TourismReason.txt b/53_King/csharp/Resources/TourismReason.txt new file mode 100644 index 00000000..b3535c3d --- /dev/null +++ b/53_King/csharp/Resources/TourismReason.txt @@ -0,0 +1,5 @@ +fish population has dwindled due to water pollution. +;air pollution is killing game bird population. +;mineral baths are being ruined by water pollution. +;unpleasant smog is discouraging sun bathers. +;hotels are looking shabby due to smog grit. diff --git a/53_King/csharp/Resources/WorkerMigration.txt b/53_King/csharp/Resources/WorkerMigration.txt new file mode 100644 index 00000000..d2f3b378 --- /dev/null +++ b/53_King/csharp/Resources/WorkerMigration.txt @@ -0,0 +1 @@ + {0} workers came to the country and \ No newline at end of file diff --git a/53_King/csharp/Result.cs b/53_King/csharp/Result.cs new file mode 100644 index 00000000..185f4617 --- /dev/null +++ b/53_King/csharp/Result.cs @@ -0,0 +1,7 @@ +namespace King; + +internal record struct Result (bool IsGameOver, string Message) +{ + internal static Result GameOver(string message) => new(true, message); + internal static Result Continue => new(false, ""); +} diff --git a/53_King/csharp/ValidityTest.cs b/53_King/csharp/ValidityTest.cs new file mode 100644 index 00000000..f1aeb5ec --- /dev/null +++ b/53_King/csharp/ValidityTest.cs @@ -0,0 +1,26 @@ +namespace King; + +internal class ValidityTest +{ + private readonly Predicate _isValid; + private readonly Func _getError; + + public ValidityTest(Predicate isValid, string error) + : this(isValid, () => error) + { + } + + public ValidityTest(Predicate isValid, Func getError) + { + _isValid = isValid; + _getError = getError; + } + + public bool IsValid(float value, IReadWrite io) + { + if (_isValid(value)) { return true; } + + io.Write(_getError()); + return false; + } +} \ No newline at end of file diff --git a/53_King/csharp/Year.cs b/53_King/csharp/Year.cs new file mode 100644 index 00000000..8e6bb0b4 --- /dev/null +++ b/53_King/csharp/Year.cs @@ -0,0 +1,154 @@ +using System.Text; + +namespace King; + +internal class Year +{ + private readonly Country _country; + private readonly IRandom _random; + private readonly IReadWrite _io; + private readonly int _plantingCost; + private readonly int _landValue; + + private float _landSold; + private float _rallodsDistributed; + private float _landPlanted; + private float _pollutionControlCost; + + private float _citizenSupport; + private int _deaths; + private float _starvationDeaths; + private int _pollutionDeaths; + private int _migration; + + public Year(Country country, IRandom random, IReadWrite io) + { + _country = country; + _random = random; + _io = io; + + _plantingCost = random.Next(10, 15); + _landValue = random.Next(95, 105); + } + + public string Status => _country.GetStatus(_landValue, _plantingCost); + + public Result? GetPlayerActions() + { + var playerSoldLand = _country.SellLand(_landValue, out _landSold); + var playerDistributedRallods = _country.DistributeRallods(out _rallodsDistributed); + var playerPlantedLand = _country.HasRallods && _country.PlantLand(_plantingCost, out _landPlanted); + var playerControlledPollution = _country.HasRallods && _country.ControlPollution(out _pollutionControlCost); + + return playerSoldLand || playerDistributedRallods || playerPlantedLand || playerControlledPollution + ? null + : Result.GameOver(Goodbye); + } + + public Result? EvaluateResults() + { + var rallodsUnspent = _country.Rallods; + + _io.WriteLine(); + _io.WriteLine(); + + return EvaluateDeaths() + ?? EvaluateMigration() + ?? EvaluateAgriculture() + ?? EvaluateTourism() + ?? DetermineResult(rallodsUnspent); + } + + public Result? EvaluateDeaths() + { + var supportedCountrymen = _rallodsDistributed / 100; + _citizenSupport = supportedCountrymen - _country.Countrymen; + _starvationDeaths = -_citizenSupport; + if (_starvationDeaths > 0) + { + if (supportedCountrymen < 50) { return Result.GameOver(EndOneThirdDead(_random)); } + _io.WriteLine(DeathsStarvation(_starvationDeaths)); + } + + var pollutionControl = _pollutionControlCost >= 25 ? _pollutionControlCost / 25 : 1; + _pollutionDeaths = (int)(_random.Next((int)_country.IndustryLand) / pollutionControl); + if (_pollutionDeaths > 0) + { + _io.WriteLine(DeathsPollution(_pollutionDeaths)); + } + + _deaths = (int)(_starvationDeaths + _pollutionDeaths); + if (_deaths > 0) + { + var funeralCosts = _deaths * 9; + _io.WriteLine(FuneralExpenses(funeralCosts)); + + if (!_country.TrySpend(funeralCosts, _landValue)) + { + _io.WriteLine(InsufficientReserves); + } + + _country.RemoveTheDead(_deaths); + } + + return null; + } + + private Result? EvaluateMigration() + { + if (_landSold > 0) + { + var newWorkers = (int)(_landSold + _random.NextFloat(10) - _random.NextFloat(20)); + if (!_country.HasWorkers) { newWorkers += 20; } + _io.Write(WorkerMigration(newWorkers)); + _country.AddWorkers(newWorkers); + } + + _migration = + (int)(_citizenSupport / 10 + _pollutionControlCost / 25 - _country.IndustryLand / 50 - _pollutionDeaths / 2); + _io.WriteLine(Migration(_migration)); + _country.Migration(_migration); + + return null; + } + + private Result? EvaluateAgriculture() + { + var ruinedCrops = (int)Math.Min(_country.IndustryLand * (_random.NextFloat() + 1.5f) / 2, _landPlanted); + var yield = (int)(_landPlanted - ruinedCrops); + var income = (int)(yield * _landValue / 2f); + + _io.Write(LandPlanted(_landPlanted)); + _io.Write(Harvest(yield, income, _country.IndustryLand > 0)); + + _country.SellCrops(income); + + return null; + } + + private Result? EvaluateTourism() + { + var reputationValue = (int)((_country.Countrymen - _migration) * 22 + _random.NextFloat(500)); + var industryAdjustment = (int)(_country.IndustryLand * 15); + var tourismIncome = Math.Abs(reputationValue - industryAdjustment); + + _io.WriteLine(TourismEarnings(tourismIncome)); + if (industryAdjustment > 0 && tourismIncome < _country.PreviousTourismIncome) + { + _io.Write(TourismDecrease(_random)); + } + + _country.EntertainTourists(tourismIncome); + + return null; + } + + private Result? DetermineResult(float rallodsUnspent) + { + if (_deaths > 200) { return Result.GameOver(EndManyDead(_deaths, _random)); } + if (_country.Countrymen < 343) { return Result.GameOver(EndOneThirdDead(_random)); } + if (rallodsUnspent / 100 > 5 && _starvationDeaths >= 2) { return Result.GameOver(EndMoneyLeftOver()); } + if (_country.Workers > _country.Countrymen) { return Result.GameOver(EndForeignWorkers(_random)); } + return null; + } +}