C# :: Aufgabe #217
4 Lösungen

Programmierung von MasterMind
Anfänger - C#
von hollst
- 02.07.2018 um 10:54 Uhr
MasterMind ist ein deduktives Spiel, das insbesondere Anfang der 70er Jahre sehr beliebt war.
Ziel des Spiels ist es, einen Geheimcode zu entschlüsseln.
In der klassischen Variante ordnet ein Spieler, genannt "codemaker", eine Sequenz von vier Symbolen
geheim an. Praktisch geschieht dies mittels unterschiedlich oder teilweise bzw. gänzlich
gleich gefärbter Pins oder Murmeln in einer Reihe, wobei maximal sechs Farben zur Verfügung stehen.
Ein zweiter Spieler, genannt "codebreaker", versucht durch Testmuster, gleichfalls bestehend aus vier gereihten
Symbolen (Pins/Murmeln), den Code zu erraten. Hätte der codebreaker 6 * 6 * 6 * 6 = 1.296 Versuche zur Verfügung,
wäre dies kein Problem, allerdings wird die Anzahl der Tests als Spielregel auf zwölf (manchmal weniger) begrenzt.
Als Responds auf ein Testmuster muss der codemaker zwei Informationen geben:
1.) Wieviele Symbole im Testmuster stehen an exakt der gleicher Stelle wie im Geheimmuster (im Falle vier, wäre das
Spiel für den codebreaker gewonnen) und
2.) wieviele Symbole (Pin-/Murmelfarben) sind sowohl im Geheim- als auch im Testmuster
vorhanden, allerdings nicht in übereinstimmender Position in der Reihe.
Die Programmieraufgabe bestehe nun darin, MasterMind interaktiv am PC zu simulieren, wobei der Computer als
codemaker fungiert, der zunächst per Zufallsgenerator das Geheimmuster erzeugt. Anschließend sind sukzessive
maximal zwölf Testmuster vom codebreaker (User) einzugeben und vom codemaker jeweils die zwei Fagen zu beantworten.
Anmerkung: MasterMind wurde auch von Mathematikern unter die Lupe genommen und bereits Mitte/Ende der 70er Jahre konnte
bewiesen werden, dass man es mit maximal fünf Testmustern lösen kann, sehr erstaunlich, oder? Daraufhin wurde
SuperMasterMind vorgeschlagen. Unterschied: Reihen mit fünf Symbolen und acht unterschiedlichen Pin-/Murmelfarben.
Wer MasterMind lediglich als schöne Spielerei ansieht, überlege bitte folgendes: Seit 1971 (release)
wurden bisher pro Jahr im Durchschnitt eine Million Spiele weltweit verkauft. Wärest Du der Erfinder und bekämest
als Lizenzgeber pro Exemplar 10 Cent, Du hättest finanziell ausgesorgt.
Ziel des Spiels ist es, einen Geheimcode zu entschlüsseln.
In der klassischen Variante ordnet ein Spieler, genannt "codemaker", eine Sequenz von vier Symbolen
geheim an. Praktisch geschieht dies mittels unterschiedlich oder teilweise bzw. gänzlich
gleich gefärbter Pins oder Murmeln in einer Reihe, wobei maximal sechs Farben zur Verfügung stehen.
Ein zweiter Spieler, genannt "codebreaker", versucht durch Testmuster, gleichfalls bestehend aus vier gereihten
Symbolen (Pins/Murmeln), den Code zu erraten. Hätte der codebreaker 6 * 6 * 6 * 6 = 1.296 Versuche zur Verfügung,
wäre dies kein Problem, allerdings wird die Anzahl der Tests als Spielregel auf zwölf (manchmal weniger) begrenzt.
Als Responds auf ein Testmuster muss der codemaker zwei Informationen geben:
1.) Wieviele Symbole im Testmuster stehen an exakt der gleicher Stelle wie im Geheimmuster (im Falle vier, wäre das
Spiel für den codebreaker gewonnen) und
2.) wieviele Symbole (Pin-/Murmelfarben) sind sowohl im Geheim- als auch im Testmuster
vorhanden, allerdings nicht in übereinstimmender Position in der Reihe.
Die Programmieraufgabe bestehe nun darin, MasterMind interaktiv am PC zu simulieren, wobei der Computer als
codemaker fungiert, der zunächst per Zufallsgenerator das Geheimmuster erzeugt. Anschließend sind sukzessive
maximal zwölf Testmuster vom codebreaker (User) einzugeben und vom codemaker jeweils die zwei Fagen zu beantworten.
Anmerkung: MasterMind wurde auch von Mathematikern unter die Lupe genommen und bereits Mitte/Ende der 70er Jahre konnte
bewiesen werden, dass man es mit maximal fünf Testmustern lösen kann, sehr erstaunlich, oder? Daraufhin wurde
SuperMasterMind vorgeschlagen. Unterschied: Reihen mit fünf Symbolen und acht unterschiedlichen Pin-/Murmelfarben.
Wer MasterMind lediglich als schöne Spielerei ansieht, überlege bitte folgendes: Seit 1971 (release)
wurden bisher pro Jahr im Durchschnitt eine Million Spiele weltweit verkauft. Wärest Du der Erfinder und bekämest
als Lizenzgeber pro Exemplar 10 Cent, Du hättest finanziell ausgesorgt.
Lösungen:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TYP217 { enum Color { Red, Blue, Yellow, Green, White, Magenta, Cyan, DarkCyan } class Program { static readonly int[] colorMap = new int[] { (int)ConsoleColor.Red, (int)ConsoleColor.Blue, (int)ConsoleColor.Yellow, (int)ConsoleColor.Green, (int)ConsoleColor.White, (int)ConsoleColor.Magenta, (int)ConsoleColor.Cyan, (int)ConsoleColor.DarkCyan }; static readonly Color maxC = (Color)6; //Enum.GetValues(typeof(Color)).Cast<Color>().Max(); const int codeLength = 4; const char colorChar = (char)219; static void Main() { Console.OutputEncoding = Encoding.GetEncoding(1252); Console.Clear(); Console.Write((new StringBuilder()).Append("\n-").Append('-', codeLength * 3)); Color[] input = null; Color[] code = GetCode(); for(int round = 0; round < 16; round++) { input = ReadInput(round, input); int same = CountSamePos(code, input); if(codeLength == same) { DrawHint(round, same, 0); break; } int diff = CountDiffPos(code, input); DrawHint(round, same, diff); } DrawCode(code); Console.ReadKey(true); } static Color[] ReadInput(int round, Color[] colors = null) { Color[] input = new Color[codeLength]; Console.CursorTop = 17 - round; Console.CursorLeft = 0; for(int i = 0; i < codeLength; i++) { input[i] = colors?[i] ?? 0; Console.ForegroundColor = (ConsoleColor)colorMap[(int)input[i]]; Console.Write(colorChar); Console.CursorLeft++; } Console.CursorLeft = 0; ConsoleKey key; do { key = Console.ReadKey(true).Key; int i = Console.CursorLeft / 2; if(key == ConsoleKey.UpArrow || key == ConsoleKey.DownArrow) { if(key == ConsoleKey.UpArrow) { input[i] = input[i] == maxC ? input[i] = 0 : input[i] + 1; } else { input[i] = input[i] == 0 ? input[i] = maxC : input[i] - 1; } Console.ForegroundColor = (ConsoleColor)colorMap[(int)input[i]]; Console.Write(colorChar); Console.CursorLeft--; } int cursorMax = (codeLength * 2) - 2; if(key == ConsoleKey.LeftArrow || key == ConsoleKey.RightArrow) { if(key == ConsoleKey.LeftArrow) { Console.CursorLeft = Console.CursorLeft == 0 ? cursorMax : Console.CursorLeft - 2; } else { Console.CursorLeft = Console.CursorLeft == cursorMax ? 0 : Console.CursorLeft + 2; } } } while(key != ConsoleKey.Enter); Console.CursorLeft = 0; Console.ResetColor(); return input; } static Color[] GetCode() { Random rnd = new Random(); Color[] code = new Color[codeLength]; for(int i = 0; i < codeLength; i++) code[i] = (Color)rnd.Next((int)maxC); return code; } static int CountSamePos(Color[] a, Color[] b) { if(a.Length != b.Length) throw new Exception(); int result = 0; for(int i = 0; i < a.Length; i++) { if(a[i] == b[i]) result++; } return result; } static int CountDiffPos(Color[] a, Color[] b) { if(a.Length != b.Length) throw new Exception(); List<Color> aDiff = new List<Color>(); List<Color> bDiff = new List<Color>(); for(int i = 0; i < a.Length; i++) { if(a[i] != b[i]) { aDiff.Add(a[i]); bDiff.Add(b[i]); } } int result = 0; foreach(Color color in aDiff.Concat(bDiff).Distinct()) { result += Math.Min(aDiff.Count(x => x == color), bDiff.Count(x => x == color)); } return result; } static void DrawHint(int round, int same, int diff) { StringBuilder sb = new StringBuilder(); Console.CursorTop = 17 - round; Console.CursorLeft = codeLength * 2; Console.BackgroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.Black; Console.Write(sb.Append('*', same)); sb.Clear(); Console.ForegroundColor = ConsoleColor.Red; Console.Write(sb.Append('*', diff)); while(Console.CursorLeft <= codeLength * 3) Console.Write(' '); Console.ResetColor(); } static void DrawCode(Color[] code) { Console.CursorTop = 0; Console.CursorLeft = 0; for(int i = 0; i < codeLength; i++) { Console.ForegroundColor = (ConsoleColor)colorMap[(int)code[i]]; Console.Write(colorChar); Console.CursorLeft++; } Console.ResetColor(); } } }

using System; using static System.Console; using System.Collections.Generic; using System.Text; namespace aufgabe_217_mastermind { class Program { static void Main() { Random rand = new Random(); bool bo_break = false; while (!bo_break) { Clear(); // = Console.Clear(); int[] code = new int[] { rand.Next(6), rand.Next(6), rand.Next(6), rand.Next(6) }; int[] move = new int[4]; Class_Mastermind cm = new Class_Mastermind(code); //Write(cm.getsecret()); bool bo_game_runs = true; int counter = 0, max_counter = 10; bool bo_end1 = false; bool bo_end2 = false; while (bo_game_runs) { bool bo_input_okay = false; while (!bo_input_okay) { bo_input_okay = true; Write($"give me your move (e.g. 0124 or 11 (is interpreted as 0011) or 0 (= 0000)): "); int input; bo_input_okay = int.TryParse(ReadLine(), out input); for (var i = 0; i < move.Length; i++) { move[move.Length - 1 - i] = input % 10; input /= 10; if (move[i] > 5) bo_input_okay = false; } } counter++; Clear(); bo_end1 = !cm.play(move); bo_end2 = counter < max_counter; bo_game_runs = bo_end1 && bo_end2; Write(cm.movelist(bo_with_secret: false)); } string info = "you have lost"; if(!bo_end1) info = "you have won"; WriteLine($"game over: {info}{Environment.NewLine}code was {cm.getsecret()}"); WriteLine("new game? (n = EXIT)"); ConsoleKeyInfo ki = ReadKey(false); bo_break = ki.Key == ConsoleKey.N; } } } public class Class_Mastermind { int array_length; int[] secret; List<int[]> moves = new List<int[]>(); public Class_Mastermind(int[] code) { this.array_length = code.Length; this.secret = new int[code.Length]; code.CopyTo(this.secret, 0); } public bool play(int[] move) { bool bo_result = true; int[] temp = new int[array_length + 2]; for (var i = 0; i < move.Length; i++) { if (move[i] != secret[i]) bo_result = false; temp[i + 1] = move[i]; } int[] temp1 = number_of_black_and_white(secret, move); temp[0] = temp1[0]; temp[temp.Length - 1] = temp1[1]; moves.Add(temp); return bo_result; //true: game over } public string movelist(bool bo_with_secret = false) { StringBuilder sb = new StringBuilder(); string sadd = string.Empty; for (var i = 0; i < moves.Count; i++) { if (bo_with_secret) sadd = getsecret(); sb.Append(sadd + $"{moves[i][0], 2} :"); for (var j = 1; j < moves[i].Length - 1; j++) sb.Append($"{moves[i][j], 2}"); sb.AppendLine($" :{moves[i][moves[i].Length - 1], 2}"); } return sb.ToString(); } public string getsecret() { StringBuilder sb = new StringBuilder(" "); for (var j = 0; j < secret.Length; j++) sb.Append($"{secret[j],2}"); sb.AppendLine(); return sb.ToString(); } int[] number_of_black_and_white(int[] n, int[] ns) //according to DONALD E. KNUTH; n = code; ns = pattern { int[] result_ncode = new int[n.Length + 2];//total_number_of_hits int[] result_ntest = new int[ns.Length + 2];//total_number_of_misses int counter_black = 0; for (var i = 0; i < ns.Length; i++) { if (ns[i] == n[i]) counter_black++; result_ncode[ns[i]]++; result_ntest[n[i]]++; } int[] result = new int[2]; for (var i = 0; i < this.array_length + 2; i++) result[0] += Math.Min(result_ncode[i], result_ntest[i]); result[1] = result[0] - counter_black; result[0] = counter_black; return result; } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MasterMind { class Game { private static String userSecretCode, message = "\nYou lost."; private static int counter = 12; private static String title = "MasterMind v1.0 - "; static void Main(string[] args) { Console.ForegroundColor = ConsoleColor.Green; /*-------------------------------------------------------------*/ SecretCode sc = new SecretCode(); for (int i = 0; i < counter; i++) { Console.Title = title + (counter - i) + " tries left."; try { sc.verifySecretCode((userSecretCode = Console.ReadLine())); if (userSecretCode != sc.secretCode) sc.handleUserSecretCode(userSecretCode); else { message = "You won."; break; } } catch (SecretCodeFormatException e) { Console.WriteLine(e.Message); i--; continue; } } Console.WriteLine(message + " The code was " + sc.secretCode); } public class SecretCode { public String secretCode { get; } public SecretCode() { secretCode = createSecretCode(); } public String createSecretCode() { Random rand = new Random(); return rand.Next(1, 7).ToString() + rand.Next(1, 7) + rand.Next(1, 7) + rand.Next(1, 7).ToString(); } public void handleUserSecretCode(String pUserSecretCode) { int count = 0, //right number count1 = 0; ; //right number on right spot for(int i = 0; i < 4; i++) { if (pUserSecretCode[i] == secretCode[i]) { count1++; } else { if(pUserSecretCode.Substring(i).Contains(secretCode[i])) count++; } } Console.WriteLine(count + "hits.\n" + count1 + " perfect matches.\n"); } public void verifySecretCode(String pUserSecretCode) { /*Requirements * Length = 4 * Numbers only * Highest Value = 6, Lowest Value = 1 */ if (pUserSecretCode.Length == 4) { foreach (char c in pUserSecretCode) { try { int n = Convert.ToInt32(c.ToString()); if (n < 1) throw new SecretCodeFormatException("Values are lower than 1!"); else if (n > 6) throw new SecretCodeFormatException("Values are higher than 6!"); } catch (FormatException) { throw new SecretCodeFormatException("Numbers only!"); } } } else throw new SecretCodeFormatException("SecretCode must be 4 characters!"); } } public class SecretCodeFormatException : Exception { public SecretCodeFormatException(string message) : base("\n"+message+"\n") { } } } }
Gelöst mit Windows Forms :)
C#-Code
C#-Code
C#-Code

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace _217MasterMind { public partial class mastermind : Form { Master master = new Master(); WinMaster wm = new WinMaster(); GroupBox[] farben; GroupBox[] schwarzweiss; int idx = 0; public mastermind() { InitializeComponent(); farben = new GroupBox[12] { gBoxFarbe1, gBoxFarbe2, gBoxFarbe3, gBoxFarbe4, gBoxFarbe5, gBoxFarbe6, gBoxFarbe7, gBoxFarbe8, gBoxFarbe9, gBoxFarbe10, gBoxFarbe11, gBoxFarbe12 }; schwarzweiss = new GroupBox[12] { gBoxSW1, gBoxSW2, gBoxSW3, gBoxSW4, gBoxSW5, gBoxSW6, gBoxSW7, gBoxSW8, gBoxSW9, gBoxSW10, gBoxSW11, gBoxSW12 }; } int count = 1; private void loesung() { btnLoesung1.BackColor = master.codefarben[0]; btnLoesung2.BackColor = master.codefarben[1]; btnLoesung3.BackColor = master.codefarben[2]; btnLoesung4.BackColor = master.codefarben[3]; } private void Button_Click(object sender, EventArgs e) { switch(count) { case 1: ActiveControl.BackColor = Color.Red; break; case 2: ActiveControl.BackColor = Color.Yellow; break; case 3: ActiveControl.BackColor = Color.DeepSkyBlue; break; case 4: ActiveControl.BackColor = Color.DeepPink; break; case 5: ActiveControl.BackColor = Color.MediumBlue; break; case 6: ActiveControl.BackColor = Color.GreenYellow; count = 0; break; } count++; foreach(GroupBox box in panel1.Controls) { if(box.Enabled == true) { wm.eingabe(box); } } btnCheck.Enabled = true; } private void BtnRegeln_Click(object sender, EventArgs e) { wm.spielRegel(); } private void BtnCheck_Click(object sender, EventArgs e) { if (wm.gewonnen()) { panel1.Enabled = true; loesung(); MessageBox.Show("Gewonnen!", "Sieg", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { if(farben[idx].Enabled == true) { wm.printErg(schwarzweiss[idx]); btnCheck.Enabled = false; farben[idx].Enabled = false; if(farben[idx] == gBoxFarbe12) { loesung(); MessageBox.Show("Alle Chancen aufgebraucht!"); } else { farben[idx + 1].Enabled = true; } } idx++; } } private void Btn_beenden_Click(object sender, EventArgs e) { Application.Exit(); } private void BtnNeustart_Click(object sender, EventArgs e) { Application.Restart(); } } }

using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _217MasterMind { class Master { enum Colors { Red = 1, Yellow, DeepSkyBlue, DeepPink, MediumBlue, GreenYellow, Max } public int[] zahl = new int[4]; public Color[] codefarben = new Color[4]; public Color[] pruefen; public int tmp = 0; public Master() { start(); } void start() { Random rnd = new Random(); for(int i = 0; i < zahl.Length; i++) { int zufall; do { zufall = rnd.Next((int)Colors.Max); } while (zahl.Contains(zufall)); zahl[i] = zufall; switch(zufall) { case 1: codefarben[i] = Color.Red; break; case 2: codefarben[i] = Color.Yellow; break; case 3: codefarben[i] = Color.DeepSkyBlue; break; case 4: codefarben[i] = Color.DeepPink; break; case 5: codefarben[i] = Color.MediumBlue; break; case 6: codefarben[i] = Color.GreenYellow; break; } } } public int getSchwarz(Color[] eingabe) { pruefen = new Color[4]; tmp = 0; int schwarz = 0; for(int i = 0; i < eingabe.Length; i++) { if(eingabe[i] == codefarben[i]) { schwarz++; pruefen[tmp] = eingabe[i]; tmp++; } } return schwarz; } public int getWeiss(Color[] eingabe) { int weiss = 0; for(int i = 0; i < eingabe.Length; i++) { if(codefarben.Contains(eingabe[i]) && (codefarben[i] != eingabe[i]) && (!pruefen.Contains(eingabe[i]))) { weiss++; pruefen[tmp] = eingabe[i]; tmp++; } } return weiss; } } }

using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace _217MasterMind { class WinMaster { Color[] beingabe = new Color[4]; Master m = new Master(); public int[] cheat() { return m.zahl; } public void eingabe(GroupBox Box) { //Benutzereingabe Control.ControlCollection c = Box.Controls; int tmp = 0; for(int i = 3; i >= 0; i--) { beingabe[tmp] = c[i].BackColor; tmp++; } } public bool gewonnen() { if(m.getSchwarz(beingabe) == 4) { return true; } else { return false; } } public void printErg(GroupBox aktBox) { //Anzahl weiße bzw schwarze Pins Control.ControlCollection c = aktBox.Controls; int s = m.getSchwarz(beingabe); for(int i = 0; i < s; i++) { c[i].BackColor = Color.Black; } if(s < 4) { int w = m.getWeiss(beingabe); for(int i = 0; i < w; i++) { c[i + s].BackColor = Color.White; } } } public void spielRegel() { MessageBox.Show("Zu Beginn legt der Computer einen 4-stelligen Code fest, der aus sechs möglichen Farben ausgewählt wird. Jede Farbe kommt dabei nur einmal vor.\nDer Spieler versucht, den Code herauszufinden. " + "Dazu setzt er einen gleichartigen Farbcode als Frage; beim ersten Zug blind geraten, bei den weiteren Zügen mit Hilfe der Antworten zu den vorangegangenen Zügen.\n" + "Auf jeden Zug hin bekommt der Rater die Information, wie viele Stifte er in Farbe und Position richtig gesetzt hat und wie viele Stifte zwar die richtige Farbe haben, aber an einer falschen Position stehen. " + "Ein Treffer in Farbe und Position wird durch einen schwarzen Stift angezeigt, ein farblich richtiger Stift an falscher Steller durch einen weißen Stift.\n" + "Alle Fragen und Antworten bleiben bis zum Ende des Spieles sichtbar.\nMan hat 12 versuche. ", "Regeln", MessageBoxButtons.OK, MessageBoxIcon.Information); } } }