Last active
September 22, 2021 22:46
-
-
Save shiracamus/17feaddaf5fdbbf9d74f044c6300d9e8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Model */ | |
enum Face { NONE, BLACK, WHITE, INVALID }; | |
class Piece { | |
final Face face, reverse; | |
Piece(Face face, Face reverse) { | |
this.face = face; | |
this.reverse = reverse; | |
} | |
} | |
final Piece NONE = new Piece(Face.NONE, Face.NONE), | |
BLACK = new Piece(Face.BLACK, Face.WHITE), | |
WHITE = new Piece(Face.WHITE, Face.BLACK), | |
INVALID = new Piece(Face.INVALID, Face.INVALID); | |
class Board { | |
static final int SIZE = 8; | |
private final Piece[][] cell = new Piece[SIZE][SIZE]; | |
Board() { | |
for (int y = 0; y < SIZE; y++) | |
for (int x = 0; x < SIZE; x++) | |
cell[y][x] = NONE; | |
int center = SIZE / 2 - 1; | |
cell[center + 0][center + 0] = WHITE; | |
cell[center + 0][center + 1] = BLACK; | |
cell[center + 1][center + 0] = BLACK; | |
cell[center + 1][center + 1] = WHITE; | |
} | |
Piece pieceAt(int x, int y) { | |
return isValid(x, y) ? cell[y][x] : INVALID; | |
} | |
private boolean isValid(int x, int y) { | |
return 0 <= x && x < SIZE && 0 <= y && y < SIZE; | |
} | |
boolean canPut() { | |
return canPut(BLACK) || canPut(WHITE); | |
} | |
private boolean canPut(Piece piece) { | |
for (int y = 0; y < SIZE; y++) | |
for (int x = 0; x < SIZE; x++) | |
if (canPut(piece, x, y)) return true; | |
return false; | |
} | |
private boolean canPut(Piece piece, int x, int y) { | |
if (pieceAt(x, y) != NONE) return false; | |
for (int dy = -1; dy <= 1; dy++) | |
for (int dx = -1; dx <= 1; dx++) | |
if (canReverse(piece, x, y, dx, dy)) return true; | |
return false; | |
} | |
private boolean canReverse(Piece piece, int x, int y, int dx, int dy) { | |
if (dx == 0 && dy == 0) return false; | |
if (pieceAt(x + dx, y + dy).face != piece.reverse) return false; | |
do { | |
x += dx; | |
y += dy; | |
} while (pieceAt(x, y).face == piece.reverse); | |
return pieceAt(x, y) == piece; | |
} | |
boolean put(Piece piece, int x, int y) { | |
if (!canPut(piece, x, y)) return false; | |
cell[y][x] = piece; | |
for (int dy = -1; dy <= 1; dy++) | |
for (int dx = -1; dx <= 1; dx++) | |
if (dx != 0 || dy != 0) reverse(piece, x, y, dx, dy); | |
return true; | |
} | |
private void reverse(Piece piece, int x, int y, int dx, int dy) { | |
if (!canReverse(piece, x, y, dx, dy)) return; | |
while (pieceAt(x + dx, y + dy).face == piece.reverse) { | |
y += dy; | |
x += dx; | |
cell[y][x] = piece; | |
} | |
} | |
int count(Piece piece) { | |
int num = 0; | |
for (int y = 0; y < SIZE; y++) | |
for (int x = 0; x < SIZE; x++) | |
if (cell[y][x] == piece) num++; | |
return num; | |
} | |
} | |
abstract class Player { | |
final String name; | |
final Piece piece; | |
String message = ""; | |
protected boolean thinking = false; | |
Player(String name, Piece piece) { | |
this.name = name + "(" + piece.face + ")"; | |
this.piece = piece; | |
} | |
boolean play(Board board) { // return true when turn player | |
if (thinking) return false; | |
thinking = true; | |
return think(board); | |
} | |
abstract boolean play(Board board, int x, int y); | |
abstract protected boolean think(Board board); | |
protected boolean put(Board board, int x, int y) { | |
if (thinking && board.put(piece, x, y)) { | |
thinking = false; | |
message = ""; | |
return true; | |
} | |
return false; | |
} | |
} | |
class Human extends Player { | |
Human(String name, Piece piece) { | |
super(name, piece); | |
} | |
boolean play(Board board, int x, int y) { // mouse clicked | |
if (put(board, x, y)) return true; | |
message = "Invalid place!"; | |
return false; | |
} | |
protected boolean think(Board board) { | |
return false; // wait to click mouse | |
} | |
} | |
class Beginner extends Player { | |
Beginner(String name, Piece piece) { | |
super(name, piece); | |
} | |
boolean play(Board board, int x, int y) { // mouse clicked | |
return false; // ignore mouse clicked | |
} | |
protected boolean think(Board board) { | |
while (!super.put(board, int(random(Board.SIZE)), int(random(Board.SIZE)))); | |
return true; | |
} | |
} | |
/* View */ | |
static final int CELL_SIZE = 100; | |
static final int CELL_OFFSET = CELL_SIZE / 2; | |
static final int PIECE_OFFSET = CELL_OFFSET + CELL_SIZE / 2; | |
static final int PIECE_SIZE = (int)(CELL_SIZE * 0.8); | |
static final int WINDOW_SIZE = CELL_SIZE * Board.SIZE + CELL_OFFSET * 2; | |
static final int MESSAGE_X = WINDOW_SIZE / 2; | |
static final int MESSAGE_Y = CELL_SIZE / 3; | |
static final int SCORE_X = WINDOW_SIZE / 2; | |
static final int SCORE_Y = WINDOW_SIZE - CELL_SIZE / 6; | |
void draw() { | |
clearWindow(); | |
drawBoard(); | |
drawScore(); | |
if (board.canPut()) | |
drawPlayer(); | |
else | |
drawJudgement(); | |
} | |
void clearWindow() { | |
fill(0); | |
rect(0, 0, WINDOW_SIZE, WINDOW_SIZE); | |
} | |
void drawBoard() { | |
for (int y = 0; y < Board.SIZE; y++) | |
for (int x = 0; x < Board.SIZE; x++) | |
drawCell(x, y); | |
} | |
void drawCell(int x, int y) { | |
stroke(0); | |
strokeWeight(2); | |
fill(0, 150, 0); // board green | |
rect(CELL_OFFSET + x * CELL_SIZE, CELL_OFFSET + y * CELL_SIZE, | |
CELL_SIZE, CELL_SIZE); | |
noStroke(); | |
drawPiece(x, y, board.pieceAt(x, y)); | |
} | |
void drawPiece(int x, int y, Piece piece) { | |
if (piece != BLACK && piece != WHITE) return; | |
fill(piece == BLACK ? 0 : 255); | |
ellipse(PIECE_OFFSET + x * CELL_SIZE, PIECE_OFFSET + y * CELL_SIZE, | |
PIECE_SIZE, PIECE_SIZE); | |
} | |
void drawScore() { | |
fill(255); | |
text("BLACK: " + board.count(BLACK) + " vs WHITE: " + board.count(WHITE), | |
SCORE_X, SCORE_Y); | |
} | |
void drawPlayer() { | |
fill(255); | |
text("TURN: " + player.name + " " + player.message, | |
MESSAGE_X, MESSAGE_Y); | |
} | |
void drawJudgement() { | |
fill(255, 255, 0); // yellow | |
int piece1 = board.count(player1.piece); | |
int piece2 = board.count(player2.piece); | |
text(piece1 > piece2 ? player1.name + " won!" : | |
piece1 < piece2 ? player2.name + " won!" : | |
"draw!", | |
MESSAGE_X, MESSAGE_Y); | |
} | |
/* Controller */ | |
void settings() { | |
size(WINDOW_SIZE, WINDOW_SIZE); | |
} | |
void setup() { | |
background(0); | |
colorMode(RGB, 256); | |
textSize(30); | |
textAlign(CENTER); | |
noLoop(); | |
redraw(); | |
thread("play"); | |
} | |
void play() { | |
if (player.play(board)) { | |
turn(); | |
} | |
} | |
void mousePressed() { | |
if (!board.canPut()) return; | |
int y = (mouseY - CELL_OFFSET) / CELL_SIZE; | |
int x = (mouseX - CELL_OFFSET) / CELL_SIZE; | |
if (player.play(board, x, y)) | |
turn(); | |
else | |
redraw(); | |
} | |
void turn() { | |
player.message = ""; | |
player = player == player1 ? player2 : player1; | |
if (!board.canPut(player.piece)) { | |
Player pass = player; | |
player = player == player1 ? player2 : player1; | |
if (!board.canPut(player.piece)) { | |
redraw(); | |
return; // game is over | |
} | |
player.message = "(" + pass.name + " was passed)"; | |
} | |
redraw(); | |
thread("play"); | |
} | |
Board board = new Board(); | |
Player player1 = new Human("You", BLACK); | |
Player player2 = new Beginner("Computer", WHITE); | |
Player player = player1; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment