diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6e18b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +checkers diff --git a/Makefile b/Makefile index 233cf8b..e2e26dc 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC = g++ -CFLAGS = -g -Wall -Wextra -Werror -std=c++11 +CFLAGS = -g -O0 -Wall -Wextra -Werror -std=c++11 CHECKERS_SOURCES = checker.cpp \ game.cpp \ board.cpp \ diff --git a/board.cpp b/board.cpp index acc629f..a1e3130 100644 --- a/board.cpp +++ b/board.cpp @@ -13,6 +13,10 @@ Board::Board() { } } +int Board::y_for_player(int player) const { + return player ? -1 : 1; +} + Checker* Board::get_checker_at(const int x, const int y) const { for(Checker* checker : checkers) { if(checker->get_x() == x and checker->get_y() == y) { @@ -36,6 +40,7 @@ void Board::print_board() const { player_symbols[checker->get_player()]; } + printf("========\n"); printf(" "); for(size_t i = 0; i < NUM_COLS; i++) { printf("%zu ", i); @@ -52,6 +57,7 @@ void Board::print_board() const { } printf("\n"); } + printf("========\n"); } int Board::player_checker_count(const int playernum) const { @@ -67,12 +73,13 @@ int Board::player_checker_count(const int playernum) const { int Board::get_capture_vector(const Checker& checker) const { int x = checker.get_x(); int y = checker.get_y(); - for(size_t i = 0; i < 2; i++) { - int x_offset = vectors[checker.get_player()][i][0]; - int y_offset = vectors[checker.get_player()][i][1]; + int y_offset = y_for_player(checker.get_player()); + + for(int i = 0; i < 2; i++) { + int x_offset = 1 - i * 2; Checker* victim = get_checker_at(x + x_offset, - y + y_offset); + y + y_offset); if(not victim or victim->get_player() == checker.get_player()) { continue; @@ -82,7 +89,7 @@ int Board::get_capture_vector(const Checker& checker) const { victim->get_y() + y_offset); if(not space) { //Empty space - return i; + return i; } } return -1; @@ -116,9 +123,9 @@ bool Board::can_player_move(const int playernum) const { if(checker->get_player() != playernum) { continue; } + int new_y = checker->get_y() + y_for_player(playernum); for(size_t i = 0; i < 2; i++) { - int new_x = checker->get_x() + vectors[playernum][i][0]; - int new_y = checker->get_y() + vectors[playernum][i][1]; + int new_x = checker->get_x() + 1 - i * 2; if(not is_position_legal(new_x, new_y)) { continue; } @@ -135,10 +142,13 @@ bool Board::is_position_legal(const int x, const int y) const { } bool Board::is_move_legal(const Checker& piece, const int vector, const int player) const { + int y_dir = y_for_player(player); + int x_dir = vector ? 1 : -1; + return is_move_legal(piece.get_x(), piece.get_y(), - piece.get_x() + vectors[player][vector][0], - piece.get_y() + vectors[player][vector][1], - player); + piece.get_x() + x_dir, + piece.get_y() + y_dir, + player); } bool Board::is_move_legal(const int x_start, const int y_start, @@ -184,28 +194,25 @@ bool Board::can_player_move_piece(const Checker& checker, const int player) cons } bool Board::can_player_move_piece(const int x, const int y, const int player) const { - if(not is_position_legal(x, y)) { - return false; - } Checker* checker = get_checker_at(x, y); - if(not checker) { - return false; - } - if(checker->get_player() != player) { + if (!checker || (checker->get_player() != player)) { return false; } + int y_dir = y_for_player(player); + for(size_t i = 0; i < 2; i++) { - if(not get_checker_at(x + vectors[player][i][0], - y + vectors[player][i][1])) { + int x_dir = 1 - i * 2; + + if(is_position_legal(x + x_dir, y + y_dir) && + not get_checker_at(x + x_dir, y + y_dir)) { return true; - } else if(is_position_legal(x + vectors[player][i][0] * 2, - y + vectors[player][i][1] * 2)) { - Checker* space = get_checker_at(x + vectors[player][i][0] * 2, - y + vectors[player][i][1] * 2); - Checker* victim = get_checker_at(x + vectors[player][i][0], - y + vectors[player][i][1]); + } + if(is_position_legal(x + x_dir * 2, y + y_dir * 2)) { + Checker* space = get_checker_at(x + x_dir * 2, + y + y_dir * 2); + Checker* victim = get_checker_at(x + x_dir, y + y_dir); if(victim and victim->get_player() != player and not space) { return true; } @@ -220,8 +227,9 @@ bool Board::can_player_move_piece(const int x, const int y, const int player) co void Board::make_capture(Checker& piece, const int vector) { int start_x = piece.get_x(); int start_y = piece.get_y(); - int offset_x = vectors[piece.get_player()][vector][0]; - int offset_y = vectors[piece.get_player()][vector][1]; + int player = piece.get_player(); + int offset_x = vector ? 1 : -1; + int offset_y = y_for_player(player); kill_checker_at(start_x + offset_x, start_y + offset_y); piece.set_x(start_x + offset_x * 2); piece.set_y(start_y + offset_y * 2); @@ -259,7 +267,7 @@ void Board::make_random_move(const int player) { if(!is_move_legal(*piece, direction, player)) { direction = (direction + 1) % 2; } - piece->set_x(piece->get_x() + vectors[player][direction][0]); - piece->set_y(piece->get_y() + vectors[player][direction][1]); + piece->set_x(piece->get_x() + (direction ? 1 : -1)); + piece->set_y(piece->get_y() + y_for_player(player)); } } diff --git a/board.h b/board.h index 42b4d04..e0166ec 100644 --- a/board.h +++ b/board.h @@ -18,21 +18,11 @@ const int player1_start_y[NUM_CHECKERS] = {7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5}; const int player_symbols[NUM_PLAYERS] = {'X', 'O'}; -const int vectors[2][2][2] = { - { - {-1, 1}, //only for player 0 - {1, 1}, //only for player 0 - }, - { - {-1, -1}, //only for player 1 - {1, -1}, //only for player1 - }, -}; - - class Board { private: unordered_set checkers; + int y_for_player(int player) const; + public: Board(); Checker* get_checker_at(const int x, const int y) const; diff --git a/checkers b/checkers deleted file mode 100755 index a2a93b9..0000000 Binary files a/checkers and /dev/null differ diff --git a/checkers.py b/checkers.py new file mode 100755 index 0000000..a8d96c6 --- /dev/null +++ b/checkers.py @@ -0,0 +1,159 @@ +#!/usr/bin/python + +import sys + +class Checkboard(object): + def __init__(self): + self.whites = set() # All white pieces, tuples of coordinates + self.blacks = set() # All black pieces, tuples of coordinates + self.checkers = {} # The entire board, only taken spots, dictionary of dictionaries [row][col] + + # Populate the board + for row in range(8): + self.checkers[row] = {} + for col in range(8): + if row < 3: + piece = 'w' + s = self.whites + elif row > 4: + piece = 'b' + s = self.blacks + else: + continue + if not ((col ^ row) & 1): # Only every other cell gets a piece + self.checkers[row][col] = piece + s.add((row, col)) + + def __str__(self): + separator = ' ' + '+-' * 8 + '+\n' + board = separator + for row in range(7, -1, -1): + board += str(row) + for col in range(8): + if (row ^ col) & 1: + check = ' ' + else: + check = '.' + board += '|' + self.checkers[row].get(col, check) + board += '|\n' + separator + board += ' ' + for col in range(8): + board += ' ' + str(col) + return board + + # Return true if the piece in [row][col] was able to capture an opponent piece + def captured(self, row, col, rowdir, coldir, mycolor): + nnc = col + 2 * coldir + nnr = row + 2 * rowdir + if nnc < 0 or nnc > 7 or nnr < 0 or nnr > 7: + return False # this piece can not capture + + nc = col + coldir + nr = row + rowdir + + if nnc in self.checkers[nnr] or not nc in self.checkers[nr] or self.checkers[nr][nc] == mycolor: + return False # there is nothing to capture + + # Determine the this and other sets + if mycolor == 'w': + os = self.blacks + s = self.whites + else: + os = self.whites + s = self.blacks + + # remove the captured piece + del self.checkers[nr][nc] + os.remove((nr, nc)) + + # move our piece + self.checkers[nnr][nnc] = mycolor + del self.checkers[row][col] + s.remove((row, col)) + s.add((nnr, nnc)) + return True + + # return true if piece in [row][col] was able to move + def moved(self, row, col, rowdir, coldir, mycolor): + nr = row + rowdir + nc = col + coldir + + if nr < 0 or nr > 7 or nc < 0 or nc > 7: + return False # no chance to move + + if nc not in self.checkers[nr]: + del self.checkers[row][col] + self.checkers[nr][nc] = mycolor + if mycolor == 'w': + s = self.whites + else: + s = self.blacks + s.remove((row, col)) + s.add((nr, nc)) + return True + return False + + + def try_capture(self, row, col, rowdir, color): + for coldir in (1, -1): + if self.captured(row, col, rowdir, coldir, color): + return True + return False + + def try_move(self, row, col, rowdir, color): + for coldir in (1, -1): + if self.moved(row, col, rowdir, coldir, color): + return True + return False + + def move(self, color): + # create a copy set, so that we can iterate over it and modify the original set + if color == 'w': + cs = set(self.whites) + rowdir = 1 + else: + cs = set(self.blacks) + rowdir = -1 + + # Captures when possible must proceed, try them first + for row, col in cs: + if self.try_capture(row, col, rowdir, color): + return True + for row, col in cs: + if self.try_move(row, col, rowdir, color): + return True + return False + + +def get_coord(cb): + while True: + sys.stdout.write('Your move: ') + sys.stdout.flush() + try: + line = sys.stdin.readline().strip() + except KeyboardInterrupt: + print + sys.exit(1) + if len(line) == 3: + row, col, dir = (int(x) for x in line) + if cb.checkers[row].get(col, '') == 'b' and dir < 2: + return row, col, dir * 2 - 1 + sys.stdout.write('Invalid input, try again\n') + +cb = Checkboard() +keepgoing = True +while keepgoing: + if True: # Computer vs human + if not cb.move('w'): + break + print cb + row, col, dir = get_coord(cb) + if cb.captured(row, col, -1, dir, 'b') or cb.moved(row, col, -1, dir, 'b'): + continue + break + for color in ('w', 'b'): + print '\nbefore %s move :' % color + print cb + if not cb.move(color): + keepgoing = False + break diff --git a/game.cpp b/game.cpp index 5acaa1e..f490a60 100644 --- a/game.cpp +++ b/game.cpp @@ -12,7 +12,6 @@ int Game::play() { int turn = 0; board.print_board(); - printf("========\n"); while(board.can_player_move(0) and board.can_player_move(1)) { if(turn == 0) { @@ -22,9 +21,7 @@ int Game::play() { } turn = (turn + 1) % NUM_PLAYERS; - printf("========\n"); board.print_board(); - printf("========\n"); } printf("Game over!"); @@ -47,6 +44,23 @@ void Game::get_player0_move() { board.make_random_move(0); } +void Game::input_next_move(int &start_x, int &start_y, int &end_x, int &end_y) +{ + string ui; + cin >> ui; + + start_x = -1; + + if (ui.length() != 4) { + return; + } + + start_x = ui[0] - '0'; + start_y = ui[1] - '0'; + end_x = ui[2] - '0'; + end_y = ui[3] - '0'; +} + void Game::get_player1_move() { //Code to get player moves int start_x = -1, start_y = -1, end_x = -1, end_y = -1; @@ -54,21 +68,15 @@ void Game::get_player1_move() { printf("Human's turn\n"); while(true) { - do { - do { - printf("X position of piece to move: "); - cin >> start_x; - printf("Y position of piece to move: "); - cin >> start_y; - } while(not board.can_player_move_piece(start_x, start_y, 1)); - - do { - printf("X position of destination: "); - cin >> end_x; - printf("Y position of destination: "); - cin >> end_y; - } while(not board.is_position_legal(end_x, end_y)); - } while(not board.is_move_legal(start_x, start_y, end_x, end_y, 1)); + while (true) { + printf("Enter your next move (4 symbols, from XY to XY): "); + input_next_move(start_x, start_y, end_x, end_y); + if (board.can_player_move_piece(start_x, start_y, 1) && + board.is_position_legal(end_x, end_y) && + board.is_move_legal(start_x, start_y, end_x, end_y, 1)) + break; + printf("this seems illegal\n"); + } Checker* checker = board.get_checker_at(start_x, start_y); diff --git a/game.h b/game.h index 6114def..fbf142e 100644 --- a/game.h +++ b/game.h @@ -8,6 +8,7 @@ class Game { Board board; void get_player0_move(); void get_player1_move(); + void input_next_move(int &xs, int &ys, int &xe, int &ye); public: Game(); int play();