Last active
June 3, 2020 22:39
-
-
Save JeremyRubin/6adbda3a7369163866af89d7da5d51ea to your computer and use it in GitHub Desktop.
This file contains 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
Board = int | |
def NewBoard(): | |
# 9*2 digits long | |
return 0b1_000_000_000_000_000_000 | |
def all_filled(board: Board) -> bool: | |
return (board & 0b111111111) | ((board >> 9) & 0b111111111) == 0b111111111 | |
winning_patterns = [ | |
0b111_000_000, | |
0b000_111_000, | |
0b000_000_111, | |
0b100_100_100, | |
0b010_010_010, | |
0b001_001_001, | |
0b100_010_001, | |
0b001_010_100, | |
] | |
def winner(board: Board) -> Optional[bool]: | |
for pat in winning_patterns: | |
if pat & (board & 0b111111111) == pat: return False | |
if pat & ((board>>9) & 0b111111111) == pat: return True | |
return None | |
cache = {} | |
def TicTacToeState(board: Board, player: bool): | |
if board in cache: | |
return cache[board] | |
board_won = winner(board) | |
unwinnable = board_won is None and all_filled(board) | |
shift = 9*player | |
filled = board | (board>>9) | |
moves = [board | ((1<<j)<<(shift)) for j in range(9) if filled & (1<<j) == 0] | |
class TicTacToe(Contract): | |
class Fields: | |
amount: Amount | |
player_1: PubKey | |
player_2: PubKey | |
# declare the checks for signatures from players | |
@require | |
def player_one(self): | |
return SignedBy(self.player_1) | |
@require | |
def player_two(self): | |
return SignedBy(self.player_2) | |
@require | |
def current_player(self): | |
return SignedBy(self.player_2) if player else SignedBy(self.player_1) | |
@require | |
def next_player(self): | |
return SignedBy(self.player_2) if not player else SignedBy(self.player_1) | |
# Player 1 wins, only enabled for winning=True boards | |
if board_won == True: | |
@player_one | |
@unlock | |
def player_1_wins(self): | |
return Satisfied() | |
# Player 2 wins, only enabled for winning=False boards | |
if board_won == False: | |
@player_two | |
@unlock | |
def player_2_wins(self): | |
return Satisfied() | |
# Only enable other cases when a winner is not yet picked. | |
if board_won == None and not unwinnable: | |
# The current player has a week to select and broadcast their move | |
# between when the anymove branch is available. | |
# | |
# Either a week goes by, then their turn can be finalized, | |
# or via the coop path the signed tx gets accepted. | |
# | |
# This protects the case that a game result is contested, giving | |
# sufficient time for player 1 to pick the correct result from the | |
# ones learned. | |
@current_player | |
@guarantee | |
def boards(self): | |
for new in moves: | |
tx = TransactionTemplate() | |
tx.set_sequence(Weeks(1)) | |
tx.add_output(self.amount, TicTacToeState(new, not player)(amount=self.amount, player_1=self.player_1, player_2=self.player_2)) | |
yield tx | |
# current player can accept next player's move at any time | |
@current_player | |
@next_player | |
@guarantee | |
def coop_boards(self): | |
for new in moves: | |
tx = TransactionTemplate() | |
tx.add_output(self.amount, TicTacToeState(new, not player)(amount=self.amount, player_1=self.player_1, player_2=self.player_2)) | |
yield tx | |
# If no move is made in 2 weeks, any move can be made | |
@guarantee | |
def anymove(self): | |
for new in moves: | |
tx = TransactionTemplate() | |
tx.set_sequence(Weeks(2)) | |
tx.add_output(self.amount, TicTacToeState(new, not player)(amount=self.amount, player_1=self.player_1, player_2=self.player_2)) | |
yield tx | |
if unwinnable: | |
@guarantee | |
def tie(self): | |
t = TransactionTemplate() | |
amt = self.amount//2 | |
t.add_output(amt, PayToPubKey(amount=amt, key=self.player_1)) | |
t.add_output(amt, PayToPubKey(amount=amt, key=self.player_2)) | |
return t | |
class W: | |
Fields = TicTacToe.Fields | |
@lru_cache | |
def create_instance(self, **kwargs: Any) -> BindableContract[TicTacToe.Fields]: | |
return TicTacToe(**kwargs) | |
def __call__(self, amount, player_1, player_2): | |
return self.create_instance(amount=amount, player_1=player_1, player_2=player_2) | |
cache[board] = W() | |
return cache[board] | |
TicTacToe = TicTacToeState(NewBoard(), False) | |
TicTacToe(player_1=PubKey("01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546bff"), player_2=PubKey("01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546bff"), amount=Bitcoin(1)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment