Hunt The Wumpus/CSharp

From Rosetta Code
Revision as of 08:14, 9 September 2017 by rosettacode>Fwend (small changes)
Hunt The Wumpus/CSharp is part of Hunt_The_Wumpus. You may find other members of Hunt_The_Wumpus at Category:Hunt_The_Wumpus.

In this version, arrows only fire a distance of one room. Since bats relocate, we don't have to worry about the wumpus room being unreachable.
There are also 3 bats instead of 2, and you get 3 arrows instead of 5.

Code

Translation of: Java

<lang csharp>using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Windows.Forms;

namespace HuntTheWumpus {

   public partial class Form1 : Form
   {
       enum Hazard { Wumpus, Bat, Pit }
       static readonly string[] warnings = { "there's an awful smell", "you hear a rustling", "you feel a draft" };
       static readonly Random rand = new Random();
       readonly int roomSize = 45;
       readonly int playerSize = 16;
       bool gameOver = true;
       int currRoom, numArrows, wumpusRoom;
       List<string> messages;
       HashSet<Hazard>[] hazards;
       Font font = new Font("SansSerif", 10, FontStyle.Bold);
       public Form1()
       {
           Width = 721;
           Height = 687;
           StartPosition = FormStartPosition.CenterScreen;
           SetStyle(
               ControlStyles.AllPaintingInWmPaint |
               ControlStyles.UserPaint |
               ControlStyles.DoubleBuffer,
               true);
           MouseDown += HandleMouseDown;
       }
       private void HandleMouseDown(object sender, MouseEventArgs e)
       {
           if (gameOver)
           {
               StartNewGame();
           }
           else
           {
               int selectedRoom = -1;
               for (int i = 0; i < links.GetLength(1); i++)
               {
                   int link = links[currRoom, i];
                   int cx = rooms[link, 0];
                   int cy = rooms[link, 1];
                   if (InsideRoom(e.X,  e.Y, cx, cy))
                   {
                       selectedRoom = link;
                       break;
                   }
               }
               if (selectedRoom == -1)
               {
                   return;
               }
               if (MouseButtons.Left == e.Button)
               {
                   currRoom = selectedRoom;
                   Situation();
               }
               else if (MouseButtons.Right == e.Button)
               {
                   Shoot(selectedRoom);
               }
           }
           Refresh();
           bool InsideRoom(int ex, int ey, int cx, int cy)
           {
               return ((ex > cx && ex < cx + roomSize)
                       && (ey > cy && ey < cy + roomSize));
           }
       }
       // don't place hazards close to the starting room
       private bool TooClose(int room)
       {
           if (currRoom == room)
           {
               return true;
           }
           for (int i = 0; i < links.GetLength(1); i++)
           {
               int link = links[currRoom, i];
               if (room == link)
                   return true;
           }
           return false;
       }
       private void Shoot(int room)
       {
           if (hazards[room].Contains(Hazard.Wumpus))
           {
               messages.Add("You win! You've killed the Wumpus!");
               gameOver = true;
           }
           else
           {
               numArrows--;
               if (numArrows == 0)
               {
                   messages.Add("You ran out of arrows.");
                   gameOver = true;
               }
               else if (rand.Next(4) != 0) // 75 %
               {
                   hazards[wumpusRoom].Remove(Hazard.Wumpus);
                   wumpusRoom = links[wumpusRoom, rand.Next(3)];
                   if (wumpusRoom == currRoom)
                   {
                       messages.Add("You woke the Wumpus and he ate you");
                       gameOver = true;
                   }
                   else
                   {
                       messages.Add("You woke the Wumpus and he moved");
                       hazards[wumpusRoom].Add(Hazard.Wumpus);
                   }
               }
           }
       }
       private void Situation()
       {
           var set = hazards[currRoom];
           if (set.Contains(Hazard.Wumpus))
           {
               messages.Add("you've been eaten by the Wumpus");
               gameOver = true;
           }
           else if (set.Contains(Hazard.Pit))
           {
               messages.Add("you fell into a pit");
               gameOver = true;
           }
           else if (set.Contains(Hazard.Bat))
           {
               messages.Add("a bat dropped you in a random room");
               // teleport, but avoid 2 teleports in a row
               do
               {
                   currRoom = rand.Next(rooms.GetLength(0));
               }
               while (hazards[currRoom].Contains(Hazard.Bat));
               // relocate the bat, but not to the player room or a room with a bat
               set.Remove(Hazard.Bat);
               int newRoom;
               do
               {
                   newRoom = rand.Next(rooms.GetLength(0));
               }
               while (newRoom == currRoom || hazards[newRoom].Contains(Hazard.Bat));
               hazards[newRoom].Add(Hazard.Bat);
               // re-evaluate
               Situation();
           }
           else
           {
               // look around
               for (int i = 0; i < links.GetLength(1); i++)
               {
                   int link = links[currRoom, i];
                   foreach (var hazard in hazards[link])
                       messages.Add(warnings[(Int32)hazard]);
               }
           }
       }
       private void StartNewGame()
       {
           numArrows = 3;
           currRoom = rand.Next(rooms.GetLength(0));
           messages = new List<string>();
           hazards = new HashSet<Hazard>[rooms.GetLength(0)];
           for (int i = 0; i < rooms.GetLength(0); i++)
               hazards[i] = new HashSet<Hazard>();
           // hazards can share rooms (unless they are identical)
           int[] ordinals = { 0, 1, 1, 1, 2, 2 };
           var values = Enum.GetValues(typeof(Hazard));
           foreach (int ord in ordinals)
           {
               int room;
               do
               {
                   room = rand.Next(rooms.GetLength(0));
               }
               while (TooClose(room) || hazards[room].Contains((Hazard)values.GetValue(ord)));
               if (ord == 0)
                   wumpusRoom = room;
               hazards[room].Add((Hazard)values.GetValue(ord));
           }
           gameOver = false;
       }
       protected override void OnPaint(PaintEventArgs args)
       {
           Graphics g = args.Graphics;
           g.SmoothingMode = SmoothingMode.AntiAlias;
           g.Clear(Color.White);
           DrawRooms(g);
           if (gameOver)
           {
               DrawStartScreen(g);
           }
           else
           {
               DrawPlayer(g);
           }
           DrawMessage(g);
       }
       private void DrawPlayer(Graphics g)
       {
           int x = rooms[currRoom, 0] + (roomSize - playerSize) / 2;
           int y = rooms[currRoom, 1] + (roomSize - playerSize) - 2;
           var player = new GraphicsPath();
           player.AddLine(x, y, x + playerSize, y);
           player.AddLine(x + playerSize, y, x + playerSize / 2, y - playerSize);
           player.CloseFigure();
           g.FillPath(Brushes.White, player);
           g.DrawPath(Pens.Black, player);
       }
       private void DrawStartScreen(Graphics g)
       {
           var brush = new SolidBrush(Color.FromArgb(0xDD, Color.White));
           g.FillRectangle(brush, 0, 0, Width, Height - 60);
           g.DrawString("HUNT THE WUMPUS", font, Brushes.DarkGray, 280, 240);
           g.DrawString("left-click to move, right-click to shoot", font, Brushes.DarkGray, 230, 310);
           g.DrawString("be aware that hazards may be in the same room", font, Brushes.DarkGray, 195, 345);
           g.DrawString("click to start", font, Brushes.DarkGray, 315, 380);
       }
       private void DrawRooms(Graphics g)
       {
           var fatLine = new Pen(Color.DarkGray, 2);
           for (int i = 0; i < links.GetLength(0); i++)
           {
               for (int j = 0; j < links.GetLength(1); j++)
               {
                   int link = links[i, j];
                   int x1 = rooms[i, 0] + roomSize / 2;
                   int y1 = rooms[i, 1] + roomSize / 2;
                   int x2 = rooms[link, 0] + roomSize / 2;
                   int y2 = rooms[link, 1] + roomSize / 2;
                   g.DrawLine(fatLine, x1, y1, x2, y2);
               }
           }
           for (int i = 0; i < rooms.GetLength(0); i++)
           {
               g.FillEllipse(Brushes.Orange, rooms[i, 0], rooms[i, 1], roomSize, roomSize);
           }
           if (!gameOver)
           {
               for (int i = 0; i < links.GetLength(1); i++)
               {
                   var link = links[currRoom, i];
                   g.FillEllipse(Brushes.Magenta, rooms[link, 0], rooms[link, 1], roomSize, roomSize);
               }
           }
           for (int i = 0; i < rooms.GetLength(0); i++)
           {
               g.DrawEllipse(fatLine, rooms[i, 0], rooms[i, 1], roomSize, roomSize);
           }
       }
       private void DrawMessage(Graphics g)
       {
           if (!gameOver)
               g.DrawString("arrows  " + numArrows, font, Brushes.Black, 610, 30);
           if (messages != null)
           {
               // collapse identical messages
               messages = messages.Distinct().ToList();
               // join at most three
               var msg = string.Join(" & ", messages.Take(3));
               g.DrawString(msg, font, Brushes.Black, 20, Height - 80);
               // if there's more, print underneath
               if (messages.Count > 3)
               {
                   g.DrawString("& " + messages[3], font, Brushes.Black, 20, Height - 60);
               }
               messages.Clear();
           }
       }
       private int[,] rooms = {{334, 20}, {609, 220}, {499, 540}, {169, 540}, {62, 220},
           {169, 255}, {232, 168}, {334, 136}, {435, 168}, {499, 255}, {499, 361},
           {435, 447}, {334, 480}, {232, 447}, {169, 361}, {254, 336}, {285, 238},
           {387, 238}, {418, 336}, {334, 393}};
       private int[,] links = {{4, 7, 1}, {0, 9, 2}, {1, 11, 3}, {4, 13, 2}, {0, 5, 3},
           {4, 6, 14}, {7, 16, 5}, {6, 0, 8}, {7, 17, 9}, {8, 1, 10}, {9, 18, 11},
           {10, 2, 12}, {13, 19, 11}, {14, 3, 12}, {5, 15, 13}, {14, 16, 19},
           {6, 17, 15}, {16, 8, 18}, {19, 10, 17}, {15, 12, 18}};
   }

}</lang>