from otree.api import * doc = """ Game of Nim. """ class Constants(BaseConstants): import csv name_in_url = 'nim_one_player' players_per_group = None win_payoff = cu(10) lose_payoff = cu(0) with open('nim_one_player/settings.csv', 'r') as para: para = list(csv.DictReader(para)) num_rounds = len(para) # number of row class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): is_winner = models.BooleanField(initial=False) player_turn = models.IntegerField(initial=0) move = models.StringField(initial='') completion_code = models.StringField() row_one = models.IntegerField(initial=1) row_two = models.IntegerField(initial=3) row_three = models.IntegerField(initial=5) row_four = models.IntegerField(initial=7) game_over = models.BooleanField(initial=False) select_row = models.IntegerField(initial=0) select_row_remain = models.IntegerField(initial=0) number = models.IntegerField(initial=0) history = models.StringField(initial = '') complete = models.BooleanField(initial=False) def rational_nim(heaps): import random import functools nim_sum = functools.reduce(lambda x, y: x ^ y, heaps) if sum(heaps) == 0: return 0,0 elif nim_sum != 0: for index, heap in enumerate(heaps): target_size = heap ^ nim_sum if target_size < heap: amount_to_remove = heap - target_size return index+1, amount_to_remove else: non_zero_heap = [] for i in range(len(heaps)): if heaps[i] != 0: non_zero_heap.append(i) index = random.choice(non_zero_heap) amount_to_remove = random.randint(1,heaps[index]) return index+1, amount_to_remove def record_history(player: Player): new_history = player.history + str(player.row_one) + str(player.row_two) + str(player.row_three) + str(player.row_four) + ',' return new_history # PAGES class Game(Page): def is_displayed(player): round_row = Constants.para[player.round_number - 1] player.player_turn = int(round_row['first_mover']) player.row_one = int(round_row['row_one']) player.row_two = int(round_row['row_two']) player.row_three = int(round_row['row_three']) player.row_four = int(round_row['row_four']) return player @staticmethod def live_method(player: Player, data): print(data) row = '' number = None news = None str_to_int = {'row_one': 1, 'row_two': 2, 'row_three': 3, 'row_four': 4,} get_row = {1: player.row_one, 2: player.row_two, 3: player.row_three, 4: player.row_four} # player choose row first if (data and data in ['row_one', 'row_two', 'row_three', 'row_four']): player.number = 0 player.complete = False player.select_row = str_to_int[data] player.select_row_remain = get_row[player.select_row] # With a row being chosen, the next message is the number if (data and data in [1, 2, 3, 4, 5, 6, 7]): player.number = data player.complete = True if (data and data == 'computer_turn'): heaps = [] for i in [player.row_one, player.row_two, player.row_three, player.row_four]: heaps.append(i) computer_row, player.number = rational_nim(heaps) player.select_row = computer_row player.select_row_remain = get_row[player.select_row] player.complete = True if (data and data == 'confirm' and player.number in [1, 2, 3, 4, 5, 6, 7] and player.select_row in [1, 2, 3, 4] and player.player_turn == 1 ): number = player.number # record history player.move += '(' if player.select_row == 1: player.row_one -= number elif player.select_row == 2: player.row_two -= number elif player.select_row == 3: player.row_three -= number elif player.select_row == 4: player.row_four -= number player.move += str(player.select_row) + ',' player.move += str(number) + ')' + ',' player.history = record_history(player) news = dict(number=number, select_row = player.select_row) player.select_row = 0 player.select_row_remain = 0 player.complete = False player.player_turn = 1 - player.player_turn # winning condition if sum(remain for remain in [player.row_one, player.row_two, player.row_three, player.row_four]) == 0: player.is_winner = True player.payoff = Constants.win_payoff player.game_over = True elif (data and data == 'computer_turn_confirm'): number = player.number if player.select_row == 1: player.row_one -= number elif player.select_row == 2: player.row_two -= number elif player.select_row == 3: player.row_three -= number elif player.select_row == 4: player.row_four -= number player.history = record_history(player) # news = None news = dict(number=number, select_row = player.select_row) player.select_row = 0 player.select_row_remain = 0 player.player_turn = 1 - player.player_turn if sum(remain for remain in [player.row_one, player.row_two, player.row_three, player.row_four]) == 0: player.is_winner = False player.game_over = True else: news = None print('player turn: ', player.player_turn) print('complete: ', player.complete) print('game over: ', player.game_over) print(str(player.row_one)+str(player.row_two)+str(player.row_three)+str(player.row_four)) print('====================') return { player.id_in_group: dict( game_over=player.game_over, player_turn=player.player_turn, news=news, select_row = player.select_row, number = player.number, remain = player.select_row_remain, row_one = player.row_one, row_two = player.row_two, row_three = player.row_three, row_four = player.row_four, complete = player.complete ) } @staticmethod def vars_for_template(player: Player): row_one_image_path = '{}.png'.format(player.row_one) return dict(row_one_image_path=row_one_image_path) class ResultsWaitPage(WaitPage): pass class Results(Page): pass page_sequence = [Game, Results]