Skip to content

Instantly share code, notes, and snippets.

@shenningsgard
Created November 30, 2015 04:07
Show Gist options
  • Save shenningsgard/2e63116ca77b9d3d537e to your computer and use it in GitHub Desktop.
Save shenningsgard/2e63116ca77b9d3d537e to your computer and use it in GitHub Desktop.
A simple multithreaded Java Applet game: Four players, four threads; who will visit all of the squares first?!
package com.example.walker;
import java.awt.*;
public class GameBoard {
private WalkerGame game;
private byte[] cells;
private final int NUM_ROWS = 10
, NUM_COLS = 10
, xOffset = 20
, yOffset = 40
, winnerXOffset = 20
, winnerYOffset = 4
, xGap = 2
, yGap = 2
, cellSize = 20;
private final Color
color_none_visited = Color.DARK_GRAY
, color_all_visited = Color.MAGENTA
, color_some_visited = Color.GRAY
, color_board_bg = Color.LIGHT_GRAY;
private final Color[]
colors_one_visited = {
Color.RED
, Color.GREEN
, Color.BLUE
, Color.ORANGE
},
colors_player_outline = {
new Color(1.0f,0.25f,0.25f)
, new Color(0.25f,1.0f,0.25f)
, new Color(0.25f,0.25f,1.0f)
, new Color(1.0f,0.8f,0.2f)
};
private byte all_cells_visited_flags;
private int[] player_visited_count;
private int[][] player_positions = { {-1,-1},{-1,-1},{-1,-1},{-1,-1} }
, starting_positions = { { 0, 0},{ 0, 9},{ 9, 0},{ 9, 9} }
, finishing_positions = { {-2, 1},{-2, 2},{-2, 3},{-2, 4} };
private final int critical_section_max_player_count = 1
, max_player_count = 4;
private int critical_section_player_count = 0
, player_count = 0
, winner_id = -1;
private String current_status_msg = "Game Started"
, alert_msg = "";
private boolean isSame(int[] a, int[] b) {
if (a.length != b.length) {
return false;
}
for (int aa = 0; aa < a.length; aa++) {
if (a[aa] != b[aa]) {
return false;
}
}
return true;
}
private boolean contains(int[][] a, int[] b) {
for (int[] aa : a) {
if ( isSame(aa, b) ) {
return true;
}
}
return false;
}
private boolean isValidPosition (int[] position) {
return isValidPosition(position[0], position[1]);
}
private boolean isValidPosition (int[] position, int player_id) {
if (isValidPosition(position)) {
for (int pp = 0; pp < player_positions.length; pp++) {
if (pp != player_id &&
player_positions[pp][0] == position[0] &&
player_positions[pp][1] == position[1]) {
return false; // position is valid, but not available
}
}
return true; // position is valid and available
}
return false; // position itself is invalid
}
private boolean isValidPosition (int row, int col) {
return (row >= 0 && row < NUM_ROWS && col >= 0 && col < NUM_COLS);
}
private boolean anyPlayerHasFinished() {
return !(all_cells_visited_flags == 0x0);
}
private boolean allPlayersHaveFinished() {
for (int pp = 0; pp < player_count; pp++) {
if (player_visited_count[pp] < NUM_COLS * NUM_ROWS)
return false;
}
System.out.println("ALL PLAYERS HAVE FINISHED");
return true;
}
private boolean hasPlayerFinished(int player_id) {
return (player_visited_count[player_id] == NUM_COLS * NUM_ROWS);
}
private void resetCellValues() {
// set all cell values to zero (no visits)
for (int row = 0; row < NUM_ROWS; row++) {
for (int col = 0; col < NUM_COLS; col++) {
cells[getCellIndex(row, col)] = 0x00;
}
}
}
private void resetAllCellsVisitedFlags() {
// reset All all cells visited flags to zero (false)
all_cells_visited_flags = 0x00;
}
private void resetPlayerVisitedCount() {
for (int pp = 0; pp < max_player_count; pp++) {
player_visited_count[pp] = 0;
}
}
private void resetPlayerPositions() {
for (int pp = 0; pp < max_player_count; pp++) {
setPlayerPosition(pp, starting_positions[pp]);
}
}
private int getCellIndex(int row, int col) {
return (row * NUM_COLS) + col;
}
private byte getPlayerValue(int player_number) {
return (byte) (0x01<<player_number);
}
private void setPlayerPosition(int player_id, int row, int col) {
// set new player position
player_positions[player_id] = new int[]{row, col};
// get cell
int cell = getCellIndex(row, col);
// logical OR
cells[cell] = (byte) (cells[cell] | getPlayerValue(player_id));
// updates and repaints the board
this.game.repaint();
}
private void setPlayerPosition(int player_id, int[] position) {
setPlayerPosition(player_id, position[0], position[1]);
}
private void updateAllPlayersStatus() {
byte updated_all_cells_visited_flags = 0x0;
int[] updated_player_visited_count = new int[player_count];
for (int pp = 0; pp < player_count; pp++) {
// add player value to all_cells_visited_flags
updated_all_cells_visited_flags = (byte) (updated_all_cells_visited_flags | getPlayerValue(pp));
// resetAll player_visited_count
updated_player_visited_count[pp] = 0;
}
// check all cells for player visits
for (int row = 0; row < NUM_ROWS; row++) {
for (int col = 0; col < NUM_COLS; col++) {
int cell_value = cells[getCellIndex(row, col)];
// update players' all cells visited flag
updated_all_cells_visited_flags = (byte) (updated_all_cells_visited_flags & cell_value);
// update player visited count
for (int pv = 0; pv < player_count; pv++) {
updated_player_visited_count[pv] += ( (getPlayerValue(pv) & cell_value) == 0x0 ? 0 : 1);
}
}
}
all_cells_visited_flags = updated_all_cells_visited_flags;
System.arraycopy(updated_player_visited_count, 0, player_visited_count, 0, player_count);
// update status message
this.current_status_msg = "";
for (int pvc = 0; pvc < updated_player_visited_count.length; pvc++) {
this.current_status_msg += "P" + pvc + ": " + updated_player_visited_count[pvc] + (pvc == updated_player_visited_count.length - 1 ? "" : ",");
}
}
private void detectWinner() {
// detect winner / current game status
if (anyPlayerHasFinished()) {
if (this.winner_id == -1) {
//find the winner
for (int pp = 0; pp < player_count; pp++) {
if (this.all_cells_visited_flags == getPlayerValue(pp)) {
// set 'winner' alert message
this.alert_msg = "\nWINNER: PLAYER " + pp + "!";
// set winner ID
this.winner_id = pp;
}
}
}
else if (allPlayersHaveFinished()) {
// announce winner_id
this.alert_msg = "GAME OVER! Time for player " + this.winner_id + "'s Victory Lap!";
}
}
}
private void graphicsClearBoard(Graphics g) {
g.setColor(this.color_board_bg);
g.fillRect(0, 0, (this.cellSize + this.xOffset) * this.NUM_COLS, (this.cellSize + this.yOffset) * this.NUM_ROWS);
}
private void graphicsDrawPlayerPositionOutlines(Graphics g) {
int drawX, drawY;
String playerName;
// CREATE OUTLINES TO SHOW PLAYER POSITIONS
for (int pp = 0; pp < player_positions.length; pp++) {
if (isValidPosition(player_positions[pp])) {
drawX = xOffset + player_positions[pp][1] * (cellSize + xGap); //row
drawY = yOffset + player_positions[pp][0] * (cellSize + yGap); //col
playerName = "";
} else if (player_positions[pp][0] == -2) {
drawX = xOffset + NUM_ROWS * (cellSize + xGap) + winnerXOffset;
drawY = yOffset + player_positions[pp][1] * (cellSize + yGap + winnerYOffset);
playerName = "Player " + pp + ": #" + player_positions[pp][1];
} else {
break;
}
// create outer black outline
g.setColor(Color.BLACK);
g.fillRect(
drawX - 2,
drawY - 2,
cellSize + 4, cellSize + 4
);
// create inner player color outline
g.setColor(colors_player_outline[pp]);
g.fillRect(
drawX - 1,
drawY - 1,
cellSize + 2, cellSize + 2
);
if (!playerName.equals("")) {
g.setColor(Color.black);
g.drawString(playerName, drawX + cellSize + xGap + 10, drawY + (cellSize / 2));
}
}
}
private void graphicsDrawCurrentStatusMsg(Graphics g) {
g.setColor(Color.BLACK);
g.drawString(this.current_status_msg, xOffset, yOffset - 20);
g.drawString(this.alert_msg, xOffset, yOffset - 10);
}
private void graphicsClearCurrentStatusMsg(Graphics g) {
g.setColor(color_board_bg);
g.drawRect(20, 10, 100, yOffset - 10);
}
private void graphicsDrawCells(Graphics g) {
for (int row = 0; row < NUM_ROWS; row++) {
for (int col = 0; col < NUM_COLS; col++) {
// set cell color based on who's visited
int cell_value = cells[getCellIndex(row, col)];
if (cell_value == 0x0F) {
g.setColor(color_all_visited);
} else if (cell_value == 0x00) {
g.setColor(color_none_visited);
} else if (cell_value == 0x01) {
g.setColor(colors_one_visited[0]);
} else if (cell_value == 0x02) {
g.setColor(colors_one_visited[1]);
} else if (cell_value == 0x04) {
g.setColor(colors_one_visited[2]);
} else if (cell_value == 0x08) {
g.setColor(colors_one_visited[3]);
} else {
g.setColor(color_some_visited);
}
// draw the cell
g.fillRect(
xOffset + col * cellSize + col * xGap,
yOffset + row * cellSize + row * yGap,
cellSize, cellSize
);
}
}
}
public GameBoard (WalkerGame g) {
this.cells = new byte[100];
this.game = g;
this.player_visited_count = new int[]{0,0,0,0};
resetAll();
}
public int addPlayer() {
if (player_count < max_player_count) {
// assign player ID
int player_id = this.player_count;
// increment player count
this.player_count++;
// place player in initial position
this.setPlayerPosition(
player_id,
starting_positions[player_id][0],
starting_positions[player_id][1]
);
//return newly-assigned player ID
return player_id;
} else {
return -1;
}
}
public synchronized void drawAll(Graphics g) {
// clear the game board
graphicsClearBoard(g);
// draw the outlined current positions of each player
graphicsDrawPlayerPositionOutlines(g);
// draw all cells with updated info
graphicsDrawCells(g);
updateAllPlayersStatus();
detectWinner();
// draw the current game status
graphicsClearCurrentStatusMsg(g);
graphicsDrawCurrentStatusMsg(g);
}
public synchronized void resetAll() {
resetCellValues();
resetAllCellsVisitedFlags();
resetPlayerVisitedCount();
resetPlayerPositions();
}
public synchronized void movePlayer(int player_id, int direction) throws InterruptedException {
while (critical_section_player_count >= critical_section_max_player_count) {
wait();
}
//
// critical section
//
critical_section_player_count += 1;
int[] candidate_position = new int[]{player_positions[player_id][0],player_positions[player_id][1]};
switch (direction) {
case 0: //up
candidate_position[0] -= 1;
break;
case 1: //right
candidate_position[1] += 1;
break;
case 2: //down
candidate_position[0] += 1;
break;
case 3: //left
candidate_position[1] -= 1;
break;
default: break;
}
if (isValidPosition(candidate_position, player_id)) {
setPlayerPosition(player_id, candidate_position[0], candidate_position[1]);
}
critical_section_player_count -= 1;
//
// end critical section
//
notifyAll();
Thread.yield();
}
public synchronized void finishPlayer(int player_id) {
//check the position of each other player;
// the first open finishing position indicates the player's ranking
for (int[] finishing_position: finishing_positions) {
if ( !contains(player_positions, finishing_position) ) {
// empty finishing position found!
System.arraycopy(
finishing_position, 0,
player_positions[player_id], 0,
player_positions[player_id].length);
break;
}
}
}
public synchronized void declareWinner(int player_id) {
// set all cell values to zero (no visits)
resetCellValues();
// reset player's cells visited flags to zero (false)
all_cells_visited_flags = (byte)(all_cells_visited_flags ^ getPlayerValue(player_id));
// reset player visited count
player_visited_count[player_id] = 0;
// reset player position
player_positions[player_id] = starting_positions[player_id];
}
public synchronized boolean isGameOver() {
return allPlayersHaveFinished();
}
public synchronized boolean isGameOver(int player_id) { return hasPlayerFinished(player_id); }
public synchronized int getWinnerId() {
return this.winner_id;
}
}
package com.example.walker;
import java.awt.*;
public class Walker extends Thread {
private final GameBoard gameboard;
private int player_id;
private final int MIN_DELAY = 10
, MAX_DELAY = 300;
public Color color;
Color[] player_colors = {
Color.RED
, Color.GREEN
, Color.BLUE
, Color.ORANGE
};
private void walk() {
long waitTime = (long) ( MIN_DELAY + (Math.floor(Math.random() * (MAX_DELAY - MIN_DELAY)) ) );
try {
sleep(waitTime);
// get a new random direction
int direction = getDirection();
// try to move
gameboard.movePlayer(player_id, direction);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private int getDirection() {
return (int) Math.floor(Math.random() * 5);
}
Walker(GameBoard g) {
this.gameboard = g;
this.player_id = g.addPlayer();
this.color = player_colors[this.player_id];
}
@Override
public void run() {
// phase 1
while (!gameboard.isGameOver(this.player_id)) {
walk();
}
// give the other threads a chance
Thread.yield();
//get the player out of the way for the others to finish
gameboard.finishPlayer(this.player_id);
// phase 2
if (gameboard.getWinnerId() == this.player_id) {
while (!gameboard.isGameOver()) {
// no reason to take up a bunch of processor while
// waiting for the other threads to finish, right?
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
gameboard.declareWinner(this.player_id);
while (!gameboard.isGameOver(this.player_id)) {
walk();
}
}
}
}
package com.example.walker;
import java.applet.*;
import java.awt.*;
public class WalkerGame extends Applet{
GameBoard gameboard;
Walker walker_0
, walker_1
, walker_2
, walker_3;
public void init() {
// set up new gameboard
this.gameboard = new GameBoard(this);
// create walkers
walker_0 = new Walker(this.gameboard);
walker_1 = new Walker(this.gameboard);
walker_2 = new Walker(this.gameboard);
walker_3 = new Walker(this.gameboard);
// start all walkers
walker_0.start();
walker_1.start();
walker_2.start();
walker_3.start();
}
public void paint(Graphics g) {
update(g);
}
public void update(Graphics g) {
this.gameboard.drawAll(g);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<applet code="com/example/walker/WalkerGame.class" width="400" height="400"></applet>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment