Hunt The Wumpus/Java: Difference between revisions
Content added Content deleted
(→{{header|Java}}: added Java) |
m (fix formatting) |
||
(12 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
{{collection|Hunt_The_Wumpus}} |
|||
In this version, arrows only fire a distance of one room. |
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. <br> |
|||
⚫ | |||
<br> |
<br> |
||
==Code== |
==Code== |
||
[[File:wumpus_java.png| |
[[File:wumpus_java.png|200px|thumb|right]] |
||
{{works with|Java|8}} |
{{works with|Java|8}} |
||
< |
<syntaxhighlight lang="java">import java.awt.*; |
||
import java.awt.event.*; |
import java.awt.event.*; |
||
import java.awt.geom.Path2D; |
import java.awt.geom.Path2D; |
||
import java.util.*; |
import java.util.*; |
||
import java.util.List; |
import java.util.List; |
||
⚫ | |||
import javax.swing.*; |
import javax.swing.*; |
||
⚫ | |||
import static javax.swing.SwingUtilities.*; |
import static javax.swing.SwingUtilities.*; |
||
public class Wumpus extends JPanel { |
public class Wumpus extends JPanel { |
||
enum Hazard { |
enum Hazard { |
||
Wumpus( |
Wumpus("there's an awful smell"), |
||
⚫ | |||
Pit("you feel a draft"); |
|||
Hazard( |
Hazard(String warning) { |
||
warning = |
this.warning = warning; |
||
} |
} |
||
final String warning; |
final String warning; |
||
final String[] warnings = {"there's an awful smell", |
|||
⚫ | |||
} |
} |
||
static final Random rand = new Random(); |
static final Random rand = new Random(); |
||
final int |
final int roomSize = 45; |
||
final int playerSize = 16; |
final int playerSize = 16; |
||
Line 34: | Line 37: | ||
int currRoom, numArrows, wumpusRoom; |
int currRoom, numArrows, wumpusRoom; |
||
List<String> messages; |
List<String> messages; |
||
Set<Hazard>[] hazards; |
|||
public Wumpus() { |
public Wumpus() { |
||
setPreferredSize(new Dimension(721, |
setPreferredSize(new Dimension(721, 687)); |
||
setBackground(Color.white); |
setBackground(Color.white); |
||
setForeground(Color.lightGray); |
setForeground(Color.lightGray); |
||
Line 44: | Line 47: | ||
addMouseListener(new MouseAdapter() { |
addMouseListener(new MouseAdapter() { |
||
boolean firedArrow = false; |
|||
@Override |
@Override |
||
public void mousePressed(MouseEvent e) { |
public void mousePressed(MouseEvent e) { |
||
if (gameOver) { |
if (gameOver) { |
||
startNewGame(); |
startNewGame(); |
||
Line 73: | Line 76: | ||
} else if (isRightMouseButton(e)) { |
} else if (isRightMouseButton(e)) { |
||
shoot(selectedRoom); |
|||
firedArrow = true; |
|||
shoot(selectedRoom); |
|||
⚫ | |||
} |
} |
||
} |
} |
||
repaint(); |
repaint(); |
||
⚫ | |||
@Override |
|||
public void mouseReleased(MouseEvent e) { |
|||
firedArrow = false; |
|||
} |
} |
||
boolean insideRoom(int ex, int ey, int cx, int cy) { |
boolean insideRoom(int ex, int ey, int cx, int cy) { |
||
return ((ex > cx && ex < cx + |
return ((ex > cx && ex < cx + roomSize) |
||
&& (ey > cy && ey < cy + |
&& (ey > cy && ey < cy + roomSize)); |
||
} |
} |
||
}); |
}); |
||
Line 95: | Line 90: | ||
void startNewGame() { |
void startNewGame() { |
||
⚫ | |||
currRoom = rand.nextInt(rooms.length); |
currRoom = rand.nextInt(rooms.length); |
||
numArrows = 3; |
|||
messages = new ArrayList<>(); |
messages = new ArrayList<>(); |
||
hazards = new |
hazards = new Set[rooms.length]; |
||
for (int i = 0; i < rooms.length; i++) |
for (int i = 0; i < rooms.length; i++) |
||
hazards |
hazards[i] = EnumSet.noneOf(Hazard.class); |
||
// hazards can share rooms |
// hazards can share rooms (unless they are identical) |
||
int[] ordinals = {0, 1, 1, 1, 2, 2}; |
int[] ordinals = {0, 1, 1, 1, 2, 2}; |
||
Hazard[] values = Hazard.values(); |
Hazard[] values = Hazard.values(); |
||
Line 110: | Line 105: | ||
do { |
do { |
||
room = rand.nextInt(rooms.length); |
room = rand.nextInt(rooms.length); |
||
} while ( |
} while (tooClose(room) || hazards[room].contains(values[ord])); |
||
if (ord == 0) |
if (ord == 0) |
||
wumpusRoom = room; |
wumpusRoom = room; |
||
hazards |
hazards[room].add(values[ord]); |
||
} |
} |
||
Line 122: | Line 117: | ||
// don't place hazards close to the starting room |
// don't place hazards close to the starting room |
||
boolean |
boolean tooClose(int room) { |
||
if (currRoom == room) |
if (currRoom == room) |
||
return true; |
return true; |
||
Line 132: | Line 127: | ||
void situation() { |
void situation() { |
||
Set<Hazard> set = hazards[currRoom]; |
|||
if ( |
if (set.contains(Hazard.Wumpus)) { |
||
messages.add("you've been eaten by the Wumpus"); |
messages.add("you've been eaten by the Wumpus"); |
||
gameOver = true; |
gameOver = true; |
||
} else if ( |
} else if (set.contains(Hazard.Pit)) { |
||
messages.add("you fell into a pit"); |
messages.add("you fell into a pit"); |
||
gameOver = true; |
gameOver = true; |
||
} else if ( |
} else if (set.contains(Hazard.Bat)) { |
||
messages.add("a bat dropped you in a random room"); |
messages.add("a bat dropped you in a random room"); |
||
Line 147: | Line 143: | ||
do { |
do { |
||
currRoom = rand.nextInt(rooms.length); |
currRoom = rand.nextInt(rooms.length); |
||
} while (hazards |
} while (hazards[currRoom].contains(Hazard.Bat)); |
||
// relocate the bat, but not to the player room |
// relocate the bat, but not to the player room or a room with a bat |
||
set.remove(Hazard.Bat); |
|||
int newRoom; |
int newRoom; |
||
do { |
do { |
||
newRoom = rand.nextInt(rooms.length); |
newRoom = rand.nextInt(rooms.length); |
||
} while (newRoom == currRoom); |
} while (newRoom == currRoom || hazards[newRoom].contains(Hazard.Bat)); |
||
hazards |
hazards[newRoom].add(Hazard.Bat); |
||
// re-evaluate |
// re-evaluate |
||
Line 164: | Line 160: | ||
// look around |
// look around |
||
for (int link : links[currRoom]) { |
for (int link : links[currRoom]) { |
||
for (Hazard hazard : hazards |
for (Hazard hazard : hazards[link]) |
||
messages.add(hazard.warning); |
messages.add(hazard.warning); |
||
} |
} |
||
Line 171: | Line 167: | ||
void shoot(int room) { |
void shoot(int room) { |
||
if (hazards |
if (hazards[room].contains(Hazard.Wumpus)) { |
||
messages.add("You win! You've killed the Wumpus!"); |
messages.add("You win! You've killed the Wumpus!"); |
||
gameOver = true; |
gameOver = true; |
||
Line 181: | Line 177: | ||
gameOver = true; |
gameOver = true; |
||
} else if (rand.nextInt(4) != 0) { |
} else if (rand.nextInt(4) != 0) { // 75 % |
||
hazards |
hazards[wumpusRoom].remove(Hazard.Wumpus); |
||
wumpusRoom = links[wumpusRoom][rand.nextInt(3)]; |
wumpusRoom = links[wumpusRoom][rand.nextInt(3)]; |
||
if (wumpusRoom == currRoom) { |
if (wumpusRoom == currRoom) { |
||
messages.add("You woke the Wumpus and he ate you"); |
messages.add("You woke the Wumpus and he ate you"); |
||
gameOver = true; |
gameOver = true; |
||
} else { |
} else { |
||
messages.add("You woke the Wumpus and he moved"); |
messages.add("You woke the Wumpus and he moved"); |
||
hazards |
hazards[wumpusRoom].add(Hazard.Wumpus); |
||
} |
} |
||
} |
} |
||
Line 196: | Line 194: | ||
void drawPlayer(Graphics2D g) { |
void drawPlayer(Graphics2D g) { |
||
int x = rooms[currRoom][0] + ( |
int x = rooms[currRoom][0] + (roomSize - playerSize) / 2; |
||
int y = rooms[currRoom][1] + ( |
int y = rooms[currRoom][1] + (roomSize - playerSize) - 2; |
||
Path2D player = new Path2D.Double(); |
Path2D player = new Path2D.Double(); |
||
Line 229: | Line 227: | ||
g.setColor(Color.darkGray); |
g.setColor(Color.darkGray); |
||
g.setStroke(new BasicStroke(2)); |
g.setStroke(new BasicStroke(2)); |
||
for (int i = 0; i < links.length; i++) { |
for (int i = 0; i < links.length; i++) { |
||
for (int link : links[i]) { |
for (int link : links[i]) { |
||
int x1 = rooms[i][0] + |
int x1 = rooms[i][0] + roomSize / 2; |
||
int y1 = rooms[i][1] + |
int y1 = rooms[i][1] + roomSize / 2; |
||
int x2 = rooms[link][0] + |
int x2 = rooms[link][0] + roomSize / 2; |
||
int y2 = rooms[link][1] + |
int y2 = rooms[link][1] + roomSize / 2; |
||
g.drawLine(x1, y1, x2, y2); |
g.drawLine(x1, y1, x2, y2); |
||
} |
} |
||
Line 241: | Line 240: | ||
g.setColor(Color.orange); |
g.setColor(Color.orange); |
||
for (int[] r : rooms) |
for (int[] r : rooms) |
||
g.fillOval(r[0], r[1], |
g.fillOval(r[0], r[1], roomSize, roomSize); |
||
if (!gameOver) { |
if (!gameOver) { |
||
g.setColor(Color.magenta); |
g.setColor(Color.magenta); |
||
for (int link : links[currRoom]) |
for (int link : links[currRoom]) |
||
g.fillOval(rooms[link][0], rooms[link][1], |
g.fillOval(rooms[link][0], rooms[link][1], roomSize, roomSize); |
||
} |
} |
||
g.setColor(Color.darkGray); |
g.setColor(Color.darkGray); |
||
for (int[] r : rooms) |
for (int[] r : rooms) |
||
g.drawOval(r[0], r[1], |
g.drawOval(r[0], r[1], roomSize, roomSize); |
||
} |
} |
||
void drawMessage(Graphics2D g) { |
void drawMessage(Graphics2D g) { |
||
if (!gameOver) |
if (!gameOver) |
||
g.drawString("arrows " + |
g.drawString("arrows " + numArrows, 610, 30); |
||
if (messages != null) { |
if (messages != null) { |
||
g.setColor(Color.black); |
g.setColor(Color.black); |
||
⚫ | |||
// collapse identical messages |
|||
messages = messages.stream().distinct().collect(toList()); |
|||
// concat at most three |
|||
⚫ | |||
g.drawString(msg, 20, getHeight() - 40); |
|||
// if there's more, print underneath |
|||
if (messages.size() > 3) { |
|||
g.drawString("& " + messages.get(3), 20, getHeight() - 17); |
|||
⚫ | |||
messages.clear(); |
messages.clear(); |
||
} |
} |
||
Line 304: | Line 314: | ||
{10, 2, 12}, {13, 19, 11}, {14, 3, 12}, {5, 15, 13}, {14, 16, 19}, |
{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}}; |
{6, 17, 15}, {16, 8, 18}, {19, 10, 17}, {15, 12, 18}}; |
||
}</ |
}</syntaxhighlight> |
Latest revision as of 14:07, 29 August 2022
Hunt The Wumpus/Java 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
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.util.*;
import java.util.List;
import javax.swing.*;
import static java.util.stream.Collectors.*;
import static javax.swing.SwingUtilities.*;
public class Wumpus extends JPanel {
enum Hazard {
Wumpus("there's an awful smell"),
Bat("you hear a rustling"),
Pit("you feel a draft");
Hazard(String warning) {
this.warning = warning;
}
final String warning;
}
static final Random rand = new Random();
final int roomSize = 45;
final int playerSize = 16;
boolean gameOver = true;
int currRoom, numArrows, wumpusRoom;
List<String> messages;
Set<Hazard>[] hazards;
public Wumpus() {
setPreferredSize(new Dimension(721, 687));
setBackground(Color.white);
setForeground(Color.lightGray);
setFont(new Font("SansSerif", Font.PLAIN, 18));
setFocusable(true);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (gameOver) {
startNewGame();
} else {
int selectedRoom = -1;
int ex = e.getX();
int ey = e.getY();
for (int link : links[currRoom]) {
int cx = rooms[link][0];
int cy = rooms[link][1];
if (insideRoom(ex, ey, cx, cy)) {
selectedRoom = link;
break;
}
}
if (selectedRoom == -1)
return;
if (isLeftMouseButton(e)) {
currRoom = selectedRoom;
situation();
} else if (isRightMouseButton(e)) {
shoot(selectedRoom);
}
}
repaint();
}
boolean insideRoom(int ex, int ey, int cx, int cy) {
return ((ex > cx && ex < cx + roomSize)
&& (ey > cy && ey < cy + roomSize));
}
});
}
void startNewGame() {
numArrows = 3;
currRoom = rand.nextInt(rooms.length);
messages = new ArrayList<>();
hazards = new Set[rooms.length];
for (int i = 0; i < rooms.length; i++)
hazards[i] = EnumSet.noneOf(Hazard.class);
// hazards can share rooms (unless they are identical)
int[] ordinals = {0, 1, 1, 1, 2, 2};
Hazard[] values = Hazard.values();
for (int ord : ordinals) {
int room;
do {
room = rand.nextInt(rooms.length);
} while (tooClose(room) || hazards[room].contains(values[ord]));
if (ord == 0)
wumpusRoom = room;
hazards[room].add(values[ord]);
}
gameOver = false;
}
// don't place hazards close to the starting room
boolean tooClose(int room) {
if (currRoom == room)
return true;
for (int link : links[currRoom])
if (room == link)
return true;
return false;
}
void situation() {
Set<Hazard> 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.nextInt(rooms.length);
} 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.nextInt(rooms.length);
} while (newRoom == currRoom || hazards[newRoom].contains(Hazard.Bat));
hazards[newRoom].add(Hazard.Bat);
// re-evaluate
situation();
} else {
// look around
for (int link : links[currRoom]) {
for (Hazard hazard : hazards[link])
messages.add(hazard.warning);
}
}
}
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.nextInt(4) != 0) { // 75 %
hazards[wumpusRoom].remove(Hazard.Wumpus);
wumpusRoom = links[wumpusRoom][rand.nextInt(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);
}
}
}
}
void drawPlayer(Graphics2D g) {
int x = rooms[currRoom][0] + (roomSize - playerSize) / 2;
int y = rooms[currRoom][1] + (roomSize - playerSize) - 2;
Path2D player = new Path2D.Double();
player.moveTo(x, y);
player.lineTo(x + playerSize, y);
player.lineTo(x + playerSize / 2, y - playerSize);
player.closePath();
g.setColor(Color.white);
g.fill(player);
g.setStroke(new BasicStroke(1));
g.setColor(Color.black);
g.draw(player);
}
void drawStartScreen(Graphics2D g) {
g.setColor(new Color(0xDDFFFFFF, true));
g.fillRect(0, 0, getWidth(), getHeight() - 60);
g.setColor(Color.darkGray);
g.setFont(new Font("SansSerif", Font.BOLD, 48));
g.drawString("hunt the wumpus", 160, 240);
g.setFont(getFont());
g.drawString("left-click to move, right-click to shoot", 210, 310);
g.drawString("be aware that hazards may be in the same room", 175, 345);
g.drawString("click to start", 310, 380);
}
void drawRooms(Graphics2D g) {
g.setColor(Color.darkGray);
g.setStroke(new BasicStroke(2));
for (int i = 0; i < links.length; i++) {
for (int link : links[i]) {
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(x1, y1, x2, y2);
}
}
g.setColor(Color.orange);
for (int[] r : rooms)
g.fillOval(r[0], r[1], roomSize, roomSize);
if (!gameOver) {
g.setColor(Color.magenta);
for (int link : links[currRoom])
g.fillOval(rooms[link][0], rooms[link][1], roomSize, roomSize);
}
g.setColor(Color.darkGray);
for (int[] r : rooms)
g.drawOval(r[0], r[1], roomSize, roomSize);
}
void drawMessage(Graphics2D g) {
if (!gameOver)
g.drawString("arrows " + numArrows, 610, 30);
if (messages != null) {
g.setColor(Color.black);
// collapse identical messages
messages = messages.stream().distinct().collect(toList());
// concat at most three
String msg = messages.stream().limit(3).collect(joining(" & "));
g.drawString(msg, 20, getHeight() - 40);
// if there's more, print underneath
if (messages.size() > 3) {
g.drawString("& " + messages.get(3), 20, getHeight() - 17);
}
messages.clear();
}
}
@Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawRooms(g);
if (gameOver) {
drawStartScreen(g);
} else {
drawPlayer(g);
}
drawMessage(g);
}
public static void main(String[] args) {
invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Hunt The Wumpus");
f.setResizable(false);
f.add(new Wumpus(), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
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}};
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}};
}