C# :: Aufgabe #217

3 Lösungen Lösungen öffentlich

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.

Lösungen:

vote_ok
von DBqFetti (2410 Punkte) - 05.07.2018 um 11:31 Uhr
Quellcode ausblenden C#-Code
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();
    }
  }
}
vote_ok
von hollst (9410 Punkte) - 16.07.2018 um 11:49 Uhr
Quellcode ausblenden C#-Code
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;
        }
    }
}
vote_ok
von DrizZle (360 Punkte) - 24.08.2018 um 12:34 Uhr
Quellcode ausblenden C#-Code
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")
            {
                
            }
        }
    }
}