2022-11-19 01:23:49 -08:00
import random
import sys
import copy
import time
GREEN = 0
YELLOW = 1
WHITE = 2
BLUE = 3
RED = 4
ALL_COLORS = [ GREEN , YELLOW , WHITE , BLUE , RED ]
COLORNAMES = [ " green " , " yellow " , " white " , " blue " , " red " ]
class Card :
def __init__ ( self , color , rank ) :
self . color = color
self . rank = rank
def isColor ( self , color ) :
return self . color == color
def isRank ( self , rank ) :
return self . rank == rank
def __eq__ ( self , other ) :
if other is None : return False
if type ( other ) == tuple :
return ( self . color , self . rank ) == other
return ( self . color , self . rank ) == ( other . color , other . rank )
def __getitem__ ( self , idx ) :
if idx == 0 : return self . color
return self . rank
def __str__ ( self ) :
return COLORNAMES [ self . color ] + " " + str ( self . rank )
def __repr__ ( self ) :
return str ( ( self . color , self . rank ) )
def is_useless ( self , board ) :
2022-11-19 02:51:52 -08:00
return board [ self . color ] . rank + 1 > self . rank
2022-11-19 01:23:49 -08:00
def is_playable ( self , board ) :
return board [ self . color ] . rank + 1 == self . rank
def __iter__ ( self ) :
return iter ( [ self . color , self . rank ] )
COUNTS = [ 3 , 2 , 2 , 2 , 1 ]
# semi-intelligently format cards in any format
def f ( something ) :
if type ( something ) == list :
return list ( map ( f , something ) )
elif type ( something ) == dict :
return { k : something ( v ) for ( k , v ) in something . items ( ) }
elif type ( something ) == Card :
return str ( something )
elif type ( something ) == tuple and len ( something ) == 2 :
return ( COLORNAMES [ something [ 0 ] ] , something [ 1 ] )
return something
def make_deck ( ) :
deck = [ ]
for color in ALL_COLORS :
for rank , cnt in enumerate ( COUNTS ) :
for i in range ( cnt ) :
deck . append ( Card ( color , rank + 1 ) )
random . shuffle ( deck )
return deck
def initial_knowledge ( ) :
knowledge = [ ]
for color in ALL_COLORS :
knowledge . append ( COUNTS [ : ] )
return knowledge
def hint_color ( knowledge , color , truth ) :
result = [ ]
for col in ALL_COLORS :
if truth == ( col == color ) :
result . append ( knowledge [ col ] [ : ] )
else :
result . append ( [ 0 for i in knowledge [ col ] ] )
return result
def hint_rank ( knowledge , rank , truth ) :
result = [ ]
for col in ALL_COLORS :
colknow = [ ]
for i , k in enumerate ( knowledge [ col ] ) :
if truth == ( i + 1 == rank ) :
colknow . append ( k )
else :
colknow . append ( 0 )
result . append ( colknow )
return result
HINT_COLOR = 0
HINT_RANK = 1
PLAY = 2
DISCARD = 3
class Action ( object ) :
def __init__ ( self , type , player = None , color = None , rank = None , card_index = None ) :
self . type = type
self . player = player
self . color = color
self . rank = rank
self . card_index = card_index
def __str__ ( self ) :
if self . type == HINT_COLOR :
return " hints " + str ( self . player ) + " about all their " + COLORNAMES [ self . color ] + " cards "
if self . type == HINT_RANK :
return " hints " + str ( self . player ) + " about all their " + str ( self . rank ) + " s "
if self . type == PLAY :
return " plays card at index " + str ( self . card_index )
if self . type == DISCARD :
return " discards card at index " + str ( self . card_index )
def __eq__ ( self , other ) :
if other is None : return False
return ( self . type , self . player , self . color , self . rank , self . card_index ) == ( other . type , other . player , other . color , other . rank , other . card_index )
def format_card ( card ) :
return str ( card )
def format_hand ( hand ) :
return " , " . join ( map ( format_card , hand ) )
class Game ( object ) :
def __init__ ( self , players , log = sys . stdout , format = 0 ) :
self . players = players
self . hits = 3
self . hints = 8
self . current_player = 0
self . board = [ Card ( c , 0 ) for c in ALL_COLORS ]
self . played = [ ]
self . deck = make_deck ( )
self . extra_turns = 0
self . hands = [ ]
self . knowledge = [ ]
self . make_hands ( )
self . trash = [ ]
self . log = log
self . turn = 1
self . format = format
self . dopostsurvey = False
self . study = False
if self . format :
print ( self . deck , file = self . log )
def make_hands ( self ) :
handsize = 4
if len ( self . players ) < 4 :
handsize = 5
for i , p in enumerate ( self . players ) :
self . hands . append ( [ ] )
self . knowledge . append ( [ ] )
for j in range ( handsize ) :
self . draw_card ( i )
def draw_card ( self , pnr = None ) :
if pnr is None :
pnr = self . current_player
if not self . deck :
return
self . hands [ pnr ] . append ( self . deck [ 0 ] )
self . knowledge [ pnr ] . append ( initial_knowledge ( ) )
del self . deck [ 0 ]
def perform ( self , action ) :
for p in self . players :
p . inform ( action , self . current_player )
if format :
print ( " MOVE: " , self . current_player , action . type , action . card_index , action . player , action . color , action . rank , file = self . log )
if action . type == HINT_COLOR :
self . hints - = 1
print ( self . players [ self . current_player ] . name , " hints " , self . players [ action . player ] . name , " about all their " , COLORNAMES [ action . color ] , " cards " , " hints remaining: " , self . hints , file = self . log )
print ( self . players [ action . player ] . name , " has " , format_hand ( self . hands [ action . player ] ) , file = self . log )
for card , knowledge in zip ( self . hands [ action . player ] , self . knowledge [ action . player ] ) :
if card . color == action . color :
for i , k in enumerate ( knowledge ) :
if i != card . color :
for i in range ( len ( k ) ) :
k [ i ] = 0
else :
for i in range ( len ( knowledge [ action . color ] ) ) :
knowledge [ action . color ] [ i ] = 0
elif action . type == HINT_RANK :
self . hints - = 1
print ( self . players [ self . current_player ] . name , " hints " , self . players [ action . player ] . name , " about all their " , action . rank , " hints remaining: " , self . hints , file = self . log )
print ( self . players [ action . player ] . name , " has " , format_hand ( self . hands [ action . player ] ) , file = self . log )
for card , knowledge in zip ( self . hands [ action . player ] , self . knowledge [ action . player ] ) :
if card . rank == action . rank :
for k in knowledge :
for i in range ( len ( COUNTS ) ) :
if i + 1 != card . rank :
k [ i ] = 0
else :
for k in knowledge :
k [ action . rank - 1 ] = 0
elif action . type == PLAY :
card = self . hands [ self . current_player ] [ action . card_index ]
print ( self . players [ self . current_player ] . name , " plays " , format_card ( card ) , end = ' ' , file = self . log )
if self . board [ card . color ] [ 1 ] == card . rank - 1 :
self . board [ card . color ] = card
self . played . append ( card )
if card . rank == 5 :
self . hints + = 1
self . hints = min ( self . hints , 8 )
print ( " successfully! Board is now " , format_hand ( self . board ) , file = self . log )
else :
self . trash . append ( card )
self . hits - = 1
print ( " and fails. Board was " , format_hand ( self . board ) , file = self . log )
del self . hands [ self . current_player ] [ action . card_index ]
del self . knowledge [ self . current_player ] [ action . card_index ]
self . draw_card ( )
print ( self . players [ self . current_player ] . name , " now has " , format_hand ( self . hands [ self . current_player ] ) , file = self . log )
else :
self . hints + = 1
self . hints = min ( self . hints , 8 )
self . trash . append ( self . hands [ self . current_player ] [ action . card_index ] )
print ( self . players [ self . current_player ] . name , " discards " , format_card ( self . hands [ self . current_player ] [ action . card_index ] ) , file = self . log )
print ( " trash is now " , format_hand ( self . trash ) , file = self . log )
del self . hands [ self . current_player ] [ action . card_index ]
del self . knowledge [ self . current_player ] [ action . card_index ]
self . draw_card ( )
print ( self . players [ self . current_player ] . name , " now has " , format_hand ( self . hands [ self . current_player ] ) , file = self . log )
def valid_actions ( self ) :
valid = [ ]
for i in range ( len ( self . hands [ self . current_player ] ) ) :
valid . append ( Action ( PLAY , card_index = i ) )
valid . append ( Action ( DISCARD , card_index = i ) )
if self . hints > 0 :
for i , p in enumerate ( self . players ) :
if i != self . current_player :
for color in set ( [ card [ 0 ] for card in self . hands [ i ] ] ) :
valid . append ( Action ( HINT_COLOR , player = i , color = color ) )
for rank in set ( [ card [ 1 ] for card in self . hands [ i ] ] ) :
valid . append ( Action ( HINT_RANK , player = i , rank = rank ) )
return valid
def run ( self , turns = - 1 ) :
self . turn = 1
while not self . done ( ) and ( turns < 0 or self . turn < turns ) :
self . turn + = 1
if not self . deck :
self . extra_turns + = 1
hands = [ ]
for i , h in enumerate ( self . hands ) :
if i == self . current_player :
hands . append ( [ ] )
else :
hands . append ( h )
valid = self . valid_actions ( )
action = None
while action not in valid :
action = self . players [ self . current_player ] . get_action ( self . current_player , hands , copy . deepcopy ( self . knowledge ) , self . trash [ : ] , self . played [ : ] , self . board [ : ] , valid , self . hints , self . hits , len ( self . deck ) )
if action not in valid :
print ( " Tried to perform illegal action, retrying " )
self . perform ( action )
self . current_player + = 1
self . current_player % = len ( self . players )
print ( " Game done, hits left: " , self . hits , file = self . log )
points = self . score ( )
print ( " Points: " , points , file = self . log )
return points
def score ( self ) :
return sum ( [ card . rank for card in self . board ] )
def single_turn ( self ) :
if not self . done ( ) :
if not self . deck :
self . extra_turns + = 1
hands = [ ]
for i , h in enumerate ( self . hands ) :
if i == self . current_player :
hands . append ( [ ] )
else :
hands . append ( h )
action = self . players [ self . current_player ] . get_action ( self . current_player , hands , self . knowledge , self . trash , self . played , self . board , self . valid_actions ( ) , self . hints , self . hits , len ( self . deck ) )
self . perform ( action )
self . current_player + = 1
self . current_player % = len ( self . players )
def external_turn ( self , action ) :
if not self . done ( ) :
if not self . deck :
self . extra_turns + = 1
self . perform ( action )
self . current_player + = 1
self . current_player % = len ( self . players )
def done ( self ) :
if self . extra_turns == len ( self . players ) or self . hits == 0 :
return True
for card in self . board :
if card . rank != 5 :
return False
return True
def finish ( self ) :
if self . format :
print ( " Score " , self . score ( ) , file = self . log )
self . log . close ( )