class BoardTest(unittest.TestCase): def setUp(self): self._board = Board() def test__get_top_row(self): self._board.play_move(BoardTag.CPU, 0) self.assertEqual(self._board._get_top_row(0), 0) self._board.play_move(BoardTag.HUMAN, 0) self.assertEqual(self._board._get_top_row(0), 1) self.assertEqual(self._board._get_top_row(1), None) def test__check_column_access(self): self.assertTrue(self._board._check_column_access(0, False)) self.assertTrue(self._board._check_column_access(6, False)) self.assertFalse(self._board._check_column_access(-1, False)) self.assertFalse(self._board._check_column_access(7, False)) def test__check_row_access(self): self.assertTrue(self._board._check_row_access(0, False)) self.assertTrue(self._board._check_row_access(5, False)) self.assertFalse(self._board._check_row_access(-1, False)) self.assertFalse(self._board._check_row_access(6, False)) # Add moves to the same column to increase board row by 1 for x in xrange(7): self._board.play_move(BoardTag.CPU, 0) self.assertTrue(self._board._check_row_access(6, False)) def test_play_move(self): self._board.play_move(BoardTag.CPU, 0) self.assertEqual(self._board._board[0][0], BoardTag.CPU) self._board.play_move(BoardTag.HUMAN, 0) self.assertEqual(self._board._board[1][0], BoardTag.HUMAN) def test_reverse_move(self): self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.HUMAN, 1) self._board.play_move(BoardTag.CPU, 0) self._board.reverse_move(1) self.assertEqual(self._board._board[0][1], BoardTag.EMPTY) self.assertEqual(self._board._board[0][0], BoardTag.CPU) self.assertEqual(self._board._board[1][0], BoardTag.CPU) self._board.reverse_move(0) self.assertEqual(self._board._board[1][0], BoardTag.EMPTY) self.assertEqual(self._board._board[0][0], BoardTag.CPU) def test__get_row_tags_1(self): self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.HUMAN, 1) self._board.play_move(BoardTag.CPU, 2) self._board.play_move(BoardTag.CPU, 3) row_tags = self._board._get_row_tags(0, 0) self.assertSequenceEqual(row_tags, [BoardTag.CPU, BoardTag.HUMAN, BoardTag.CPU, BoardTag.CPU]) def test__get_row_tags_2(self): self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.CPU, 6) self._board.play_move(BoardTag.CPU, 3) row_tags = self._board._get_row_tags(0, 3) self.assertSequenceEqual(row_tags, [BoardTag.CPU, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.CPU, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.CPU]) def test__get_column_tags_1(self): self._board.play_move(BoardTag.CPU, 0) column_tags = self._board._get_column_tags(0, 0) self.assertSequenceEqual(column_tags, [BoardTag.CPU, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.EMPTY]) def test__get_column_tags_2(self): self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.HUMAN, 0) column_tags = self._board._get_column_tags(3, 0) self.assertSequenceEqual(column_tags, [BoardTag.CPU, BoardTag.HUMAN, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.EMPTY]) def test__get_diagonal_tags_left_1(self): self._board.play_move(BoardTag.CPU, 6) self._board.play_move(BoardTag.HUMAN, 3) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.CPU, 3) left_diagonal_tags = self._board._get_diagonal_tags(0, 6) self.assertSequenceEqual(left_diagonal_tags, [BoardTag.CPU, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.CPU]) def test__get_diagonal_tags_left_2(self): self._board.play_move(BoardTag.CPU, 6) self._board.play_move(BoardTag.HUMAN, 3) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.CPU, 3) left_diagonal_tags = self._board._get_diagonal_tags(3, 3) self.assertSequenceEqual(left_diagonal_tags, [BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.CPU, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.CPU]) def test__get_diagonal_tags_right_1(self): self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.HUMAN, 3) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.CPU, 3) right_diagonal_tags = self._board._get_diagonal_tags(0, 0, True) self.assertSequenceEqual(right_diagonal_tags, [BoardTag.CPU, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.CPU]) def test__get_diagonal_tags_right_2(self): self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.HUMAN, 3) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.CPU, 3) right_diagonal_tags = self._board._get_diagonal_tags(3, 3, True) self.assertSequenceEqual(right_diagonal_tags, [BoardTag.CPU, BoardTag.EMPTY, BoardTag.EMPTY, BoardTag.CPU, BoardTag.EMPTY, BoardTag.EMPTY]) def test__check4_in_row(self): test_row_1 = [BoardTag.CPU, BoardTag.CPU, BoardTag.CPU, BoardTag.CPU] self.assertTrue(Board._check4_in_list(test_row_1, BoardTag.CPU)) test_row_2 = [BoardTag.EMPTY, BoardTag.CPU, BoardTag.CPU, BoardTag.CPU, BoardTag.CPU, BoardTag.EMPTY] self.assertTrue(Board._check4_in_list(test_row_2, BoardTag.CPU)) test_row_3 = [BoardTag.CPU, BoardTag.CPU, BoardTag.EMPTY, BoardTag.CPU, BoardTag.CPU] self.assertFalse(Board._check4_in_list(test_row_3, BoardTag.CPU)) def test_check_if_finished_row(self): self._board.play_move(BoardTag.CPU, 0) finished_1 = self._board.check_if_finished(0) self.assertFalse(finished_1[0]) self._board.play_move(BoardTag.CPU, 1) self._board.play_move(BoardTag.CPU, 3) self._board.play_move(BoardTag.HUMAN, 1) self._board.play_move(BoardTag.CPU, 2) finished_2 = self._board.check_if_finished(2) self.assertTrue(finished_2[0]) self.assertEqual(finished_2[1], BoardTag.CPU) def test_check_if_finished_column(self): self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.CPU, 0) finished = self._board.check_if_finished(0) self.assertTrue(finished[0]) self.assertEqual(finished[1], BoardTag.CPU) def test_check_if_finished_diagonal(self): self._board.play_move(BoardTag.CPU, 0) self._board.play_move(BoardTag.HUMAN, 1) self._board.play_move(BoardTag.CPU, 1) self._board.play_move(BoardTag.HUMAN, 2) self._board.play_move(BoardTag.HUMAN, 2) self._board.play_move(BoardTag.CPU, 2) finished_1 = self._board.check_if_finished(2) self.assertFalse(finished_1[0]) self._board.play_move(BoardTag.HUMAN, 3) self._board.play_move(BoardTag.HUMAN, 3) self._board.play_move(BoardTag.HUMAN, 3) self._board.play_move(BoardTag.CPU, 3) finished_2 = self._board.check_if_finished(3) self.assertTrue(finished_2[0]) self.assertEqual(finished_2[1], BoardTag.CPU)
class Master(): def __init__(self, measuring): self._measuring = measuring self._board = Board() self._taskPool = TaskPool() self._log = Log('Master') self._log.debug('init') def work(self): self._log.debug('work') while self._move_cycle(): pass self._stop_workers() def _move_cycle(self): self._taskPool = TaskPool() self._print('CPU is thinking...') start_time = time.time() # Broadcast board data self._broadcast_board() # Send tasks to workers on request self._serve_tasks() column_quality = self._taskPool.calculate_quality() best_column_move = max(enumerate(column_quality), key=lambda x: x[1])[0] end_time = time.time() - start_time print (end_time if self._measuring else 'Thinking done in: %f\n' % end_time) # Print column quality for idx, quality in enumerate(column_quality): self._print('Column %d quality: %f' % (idx+1, quality)) self._print('Best column move %d\n' % (best_column_move+1)) # In case we are just measuring 1st move by CPU if self._measuring: self._stop_workers() return False self._board.play_move(BoardTag.CPU, best_column_move) self._board.print_board() self._print('') if self._board.check_if_finished(best_column_move)[0]: self._print_winner(BoardTag.CPU) return False human_column_move = self._human_turn() if human_column_move == -1: return False if self._board.check_if_finished(human_column_move)[0]: self._print_winner(BoardTag.HUMAN) return False return True def _broadcast_board(self): message = {'type': MessageType.BOARD_DATA, 'board': self._board} for x in xrange(1, comm_size): self._log.debug('BOARD_DATA to %d' % x) comm.send(message, dest=x) def _serve_tasks(self): active_workers = comm_size - 1 finished = False while not finished: self._log.debug('waiting for DATA_REQUEST') # Send tasks upon request message_status = MPI.Status() message = comm.recv(source=MPI.ANY_SOURCE, status=message_status) message_source = message_status.Get_source() if message['type'] == MessageType.TASK_REQUEST: self._log.debug('receives TASK_REQUEST from %d' % message_source) task = self._taskPool.next_task() if task is not None: message = {'type': MessageType.TASK_DATA, 'task': task} self._log.debug('sends TASK_DATA to %d' % message_source) comm.send(message, dest=message_source) else: self._log.debug('sends WAIT to %d' % message_source) comm.send({'type': MessageType.WAIT}, dest=message_source) active_workers -= 1 if active_workers == 0: finished = True elif message['type'] == MessageType.TASK_RESULT: self._log.debug('receives TASK_RESULT from %d' % message_source) self._taskPool.update_task(message['task'], message['result']) def _stop_workers(self): for x in xrange(1, comm_size): self._log.debug('STOP to %d' % x) comm.send({'type': MessageType.STOP}, dest=x) def _human_turn(self): # Take player move human_input = self._read_human_input()-1 if human_input == -1: print 'Human gave up! CPU WINS!' return human_input # play self._board.play_move(BoardTag.HUMAN, human_input) self._board.print_board() print return human_input @staticmethod def _read_human_input(): while True: print 'Human move:', human_input = raw_input() try: human_input = int(human_input) if human_input < 0 or human_input > 7: raise ValueError() return human_input except ValueError: print 'Invalid input! Enter 1-7 for column or 0 for end' @staticmethod def _print_winner(player): player_label = 'CPU' if player == BoardTag.CPU else 'Human' print '%s won!' % player_label def _print(self, message): if not self._measuring: print message
class Worker(): def __init__(self, worker_depth): self._board = Board() # This will be overwritten when BOARD_DATA is received self._worker_depth = worker_depth self._log = Log('Worker %d' % rank) self._log.debug('init') def work(self): self._log.debug('work') # Move cycle while True: message = comm.recv(source=0) if message['type'] == MessageType.BOARD_DATA: self._log.debug('receives BOARD_DATA') self._board = message['board'] elif message['type'] == MessageType.STOP: self._log.debug('receives STOP') break # Start requesting tasks while self._request_task(): pass def _request_task(self): self._log.debug('sends TASK_REQUEST') message = {'type': MessageType.TASK_REQUEST} comm.send(message, dest=0) message = comm.recv(source=0) if message['type'] == MessageType.WAIT: self._log.debug('receives WAIT') return False task = message['task'] self._log.debug('receives TASK_DATA {0}'.format(task)) # CPU first move if self._play_cpu(task): return True # Human move if self._play_human(task): return True # Evaluate in depth result = self._evaluate(BoardTag.CPU, task[1], self._worker_depth) self._board.reverse_move(task[1]) self._board.reverse_move(task[0]) self._log.debug('sends TASK_RESULT') message = {'type': MessageType.TASK_RESULT, 'task': task, 'result': result} comm.send(message, dest=0) return True def _play_cpu(self, task): self._board.play_move(BoardTag.CPU, task[0]) if self._board.check_if_finished(task[0])[0]: result = 1 self._board.reverse_move(task[0]) message = {'type': MessageType.TASK_RESULT, 'task': task, 'result': result} comm.send(message, dest=0) return True def _play_human(self, task): self._board.play_move(BoardTag.HUMAN, task[1]) if self._board.check_if_finished(task[1])[0]: result = -1 self._board.reverse_move(task[1]) self._board.reverse_move(task[0]) message = {'type': MessageType.TASK_RESULT, 'task': task, 'result': result} comm.send(message, dest=0) return True def _evaluate(self, current_player, last_played_column, depth): end_check = self._board.check_if_finished(last_played_column) if end_check[0]: return 1 if end_check[1] == BoardTag.CPU else -1 if depth == 0: return 0 total_result = 0.0 all_child_loses = True all_child_wins = True for column in xrange(7): self._board.play_move(current_player, column) next_player = BoardTag.HUMAN if current_player == BoardTag.CPU else BoardTag.CPU result = self._evaluate(next_player, column, depth-1) self._board.reverse_move(column) if result > -1: all_child_loses = False if result != 1: all_child_wins = False if result == 1 and current_player == BoardTag.HUMAN: return 1 # if win is discovered before human turn if result == -1 and current_player == BoardTag.CPU: return -1 # if lost is discovered before cpu turn total_result += result return 1 if all_child_wins else -1 if all_child_loses else total_result / 7