using System;
using System.Collections.Generic;
using System.Linq;
namespace Hammurabi
{
public static class Rules
{
///
/// Creates the initial state for a new game.
///
public static GameState BeginGame() =>
new GameState
{
Year = 0,
Population = 95,
PopulationIncrease = 5,
Starvation = 0,
Acres = 1000,
Stores = 0,
AcresPlanted = 1000,
Productivity = 3,
Spoilage = 200,
IsPlagueYear = false,
IsPlayerImpeached = false
};
///
/// Updates the game state to start a new turn.
///
public static GameState BeginTurn(GameState state, Random random) =>
state with
{
Year = state.Year + 1,
Population = (state.Population + state.PopulationIncrease - state.Starvation) / (state.IsPlagueYear ? 2 : 1),
LandPrice = random.Next(10) + 17,
Stores = state.Stores + (state.AcresPlanted * state.Productivity) - state.Spoilage,
AcresPlanted = 0,
FoodDistributed = 0
};
///
/// Attempts to purchase the given number of acres.
///
///
/// The updated game state and action result.
///
public static (GameState newState, ActionResult result) BuyLand(GameState state, int amount)
{
var price = state.LandPrice * amount;
if (price < 0)
return (state, ActionResult.Offense);
else
if (price > state.Stores)
return (state, ActionResult.InsufficientStores);
else
return (state with { Acres = state.Acres + amount, Stores = state.Stores - price }, ActionResult.Success);
}
///
/// Attempts to sell the given number of acres.
///
///
/// The updated game state and action result.
///
public static (GameState newState, ActionResult result) SellLand(GameState state, int amount)
{
var price = state.LandPrice * amount;
if (price < 0)
return (state, ActionResult.Offense);
else
if (amount >= state.Acres)
return (state, ActionResult.InsufficientLand);
else
return (state with { Acres = state.Acres - amount, Stores = state.Stores + price }, ActionResult.Success);
}
///
/// Attempts to feed the people the given number of buschels.
///
///
///
/// The updated game state and action result.
///
public static (GameState newState, ActionResult result) FeedPeople(GameState state, int amount)
{
if (amount < 0)
return (state, ActionResult.Offense);
else
if (amount > state.Stores)
return (state, ActionResult.InsufficientStores);
else
return (state with { Stores = state.Stores - amount, FoodDistributed = state.FoodDistributed + amount }, ActionResult.Success);
}
///
/// Attempts to plant crops on the given number of acres.
///
///
/// The updated game state and action result.
///
public static (GameState newState, ActionResult result) PlantCrops(GameState state, int amount)
{
var storesRequired = amount / 2;
var maxAcres = state.Population * 10;
if (amount < 0)
return (state, ActionResult.Offense);
else
if (amount > state.Acres)
return (state, ActionResult.InsufficientLand);
else
if (storesRequired > state.Stores)
return (state, ActionResult.InsufficientStores);
else
if ((state.AcresPlanted + amount) > maxAcres)
return (state, ActionResult.InsufficientPopulation);
else
return (state with
{
AcresPlanted = state.AcresPlanted + amount,
Stores = state.Stores - storesRequired,
}, ActionResult.Success);
}
///
/// Ends the current turn and returns the updated game state.
///
public static GameState EndTurn(GameState state, Random random)
{
var productivity = random.Next(1, 6);
var harvest = productivity * state.AcresPlanted;
var spoilage = random.Next(1, 6) switch
{
2 => state.Stores / 2,
4 => state.Stores / 4,
_ => 0
};
var populationIncrease= (int)((double)random.Next(1, 6) * (20 * state.Acres + state.Stores + harvest - spoilage) / state.Population / 100 + 1);
var plagueYear = random.Next(20) < 3;
var peopleFed = state.FoodDistributed / 20;
var starvation = peopleFed < state.Population ? state.Population - peopleFed : 0;
var impeached = starvation > state.Population * 0.45;
return state with
{
Productivity = productivity,
Spoilage = spoilage,
PopulationIncrease = populationIncrease,
Starvation = starvation,
IsPlagueYear = plagueYear,
IsPlayerImpeached = impeached
};
}
///
/// Examines the game's history to arrive at the final result.
///
public static GameResult GetGameResult(IEnumerable history, Random random)
{
var (_, averageStarvationRate, totalStarvation, finalState) = history.Aggregate(
(count: 0, starvationRate: 0, totalStarvation: 0, finalState: default(GameState)),
(stats, state) =>
(
stats.count + 1,
((stats.starvationRate * stats.count) + (state.Starvation * 100 / state.Population)) / (stats.count + 1),
stats.totalStarvation + state.Starvation,
state
));
var acresPerPerson = finalState.Acres / finalState.Population;
var rating = finalState.IsPlayerImpeached ?
PerformanceRating.Disgraceful :
(averageStarvationRate, acresPerPerson) switch
{
(> 33, _) => PerformanceRating.Disgraceful,
(_, < 7) => PerformanceRating.Disgraceful,
(> 10, _) => PerformanceRating.Bad,
(_, < 9) => PerformanceRating.Bad,
(> 3, _) => PerformanceRating.Ok,
(_, < 10) => PerformanceRating.Ok,
_ => PerformanceRating.Terrific
};
var assassins = rating == PerformanceRating.Ok ?
random.Next(0, (int)(finalState.Population * 0.8)) : 0;
return new GameResult
{
Rating = rating,
AcresPerPerson = acresPerPerson,
FinalStarvation = finalState.Starvation,
TotalStarvation = totalStarvation,
AverageStarvationRate = averageStarvationRate,
Assassins = assassins,
WasPlayerImpeached = finalState.IsPlayerImpeached
};
}
}
}