mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 07:10:42 -08:00
174 lines
6.3 KiB
C#
174 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Game
|
|
{
|
|
// MASTERMIND II
|
|
// STEVE NORTH
|
|
// CREATIVE COMPUTING
|
|
// PO BOX 789-M MORRISTOWN NEW JERSEY 07960
|
|
class Program
|
|
{
|
|
public const int MaximumGuesses = 10;
|
|
|
|
static void Main()
|
|
{
|
|
var (codeFactory, rounds) = StartGame();
|
|
|
|
var random = new Random();
|
|
var humanScore = 0;
|
|
var computerScore = 0;
|
|
|
|
for (var round = 1; round <= rounds; ++round)
|
|
{
|
|
View.ShowStartOfRound(round);
|
|
|
|
if (!HumanTakesTurn())
|
|
return;
|
|
|
|
while (!ComputerTakesTurn())
|
|
View.ShowInconsistentInformation();
|
|
}
|
|
|
|
View.ShowScores(humanScore, computerScore, isFinal: true);
|
|
|
|
/// <summary>
|
|
/// Gets the game start parameters from the user.
|
|
/// </summary>
|
|
(CodeFactory codeFactory, int rounds) StartGame()
|
|
{
|
|
View.ShowBanner();
|
|
|
|
var colors = Controller.GetNumberOfColors();
|
|
var positions = Controller.GetNumberOfPositions();
|
|
var rounds = Controller.GetNumberOfRounds();
|
|
|
|
var codeFactory = new CodeFactory(positions, colors);
|
|
|
|
View.ShowTotalPossibilities(codeFactory.Possibilities);
|
|
View.ShowColorTable(codeFactory.Colors);
|
|
|
|
return (codeFactory, rounds);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the human's turn.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// True if thue human completed his or her turn and false if
|
|
/// he or she quit the game.
|
|
/// </returns>
|
|
bool HumanTakesTurn()
|
|
{
|
|
// Store a history of the human's guesses (used for the show
|
|
// board command below).
|
|
var history = new List<TurnResult>();
|
|
var code = codeFactory.Create(random);
|
|
var guessNumber = default(int);
|
|
|
|
for (guessNumber = 1; guessNumber <= MaximumGuesses; ++guessNumber)
|
|
{
|
|
var guess = default(Code);
|
|
|
|
while (guess is null)
|
|
{
|
|
switch (Controller.GetCommand(guessNumber, codeFactory.Positions, codeFactory.Colors))
|
|
{
|
|
case (Command.MakeGuess, Code input):
|
|
guess = input;
|
|
break;
|
|
case (Command.ShowBoard, _):
|
|
View.ShowBoard(history);
|
|
break;
|
|
case (Command.Quit, _):
|
|
View.ShowQuitGame(code);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var (blacks, whites) = code.Compare(guess);
|
|
if (blacks == codeFactory.Positions)
|
|
break;
|
|
|
|
View.ShowResults(blacks, whites);
|
|
|
|
history.Add(new TurnResult(guess, blacks, whites));
|
|
}
|
|
|
|
if (guessNumber <= MaximumGuesses)
|
|
View.ShowHumanGuessedCode(guessNumber);
|
|
else
|
|
View.ShowHumanFailedToGuessCode(code);
|
|
|
|
humanScore += guessNumber;
|
|
|
|
View.ShowScores(humanScore, computerScore, isFinal: false);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes the computers turn.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// True if the computer completes its turn successfully and false
|
|
/// if it does not (due to human error).
|
|
/// </returns>
|
|
bool ComputerTakesTurn()
|
|
{
|
|
var isCandidate = new bool[codeFactory.Possibilities];
|
|
var guessNumber = default(int);
|
|
|
|
Array.Fill(isCandidate, true);
|
|
|
|
View.ShowComputerStartTurn();
|
|
Controller.WaitUntilReady();
|
|
|
|
for (guessNumber = 1; guessNumber <= MaximumGuesses; ++guessNumber)
|
|
{
|
|
// Starting with a random code, cycle through codes until
|
|
// we find one that is still a candidate solution. If
|
|
// there are no remaining candidates, then it implies that
|
|
// the user made an error in one or more responses.
|
|
var codeNumber = EnumerableExtensions.Cycle(random.Next(codeFactory.Possibilities), codeFactory.Possibilities)
|
|
.FirstOrDefault(i => isCandidate[i], -1);
|
|
|
|
if (codeNumber < 0)
|
|
return false;
|
|
|
|
var guess = codeFactory.Create(codeNumber);
|
|
|
|
var (blacks, whites) = Controller.GetBlacksWhites(guess);
|
|
if (blacks == codeFactory.Positions)
|
|
break;
|
|
|
|
// Mark codes which are no longer potential solutions. We
|
|
// know that the current guess yields the above number of
|
|
// blacks and whites when compared to the solution, so any
|
|
// code that yields a different number of blacks or whites
|
|
// can't be the answer.
|
|
foreach (var (candidate, index) in codeFactory.EnumerateCodes().Select((candidate, index) => (candidate, index)))
|
|
{
|
|
if (isCandidate[index])
|
|
{
|
|
var (candidateBlacks, candidateWhites) = guess.Compare(candidate);
|
|
if (blacks > candidateBlacks || whites > candidateWhites)
|
|
isCandidate[index] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (guessNumber <= MaximumGuesses)
|
|
View.ShowComputerGuessedCode(guessNumber);
|
|
else
|
|
View.ShowComputerFailedToGuessCode();
|
|
|
|
computerScore += guessNumber;
|
|
View.ShowScores(humanScore, computerScore, isFinal: false);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|