check if it works
This commit is contained in:
parent
3beaec0cfd
commit
e8fb679f5e
6 changed files with 161 additions and 96 deletions
|
@ -9,6 +9,7 @@ open Yojson.Basic.Util;;
|
|||
|
||||
let read_json () =
|
||||
let json = Yojson.Basic.from_channel stdin in let open Yojson.Basic.Util in
|
||||
(* let json = Yojson.Basic.from_file "../ono_sendai/debug.json" in let open Yojson.Basic.Util in *)
|
||||
let make_card l =
|
||||
let hd = List.hd l |> to_string in
|
||||
let tl = List.tl l |> List.hd |> to_int in
|
||||
|
@ -29,9 +30,14 @@ let to_json (table:Table.table) =
|
|||
cards_to_json tc.cards in
|
||||
`List (List.map (fun tcl -> tcards_to_json tcl) table.cards);;
|
||||
|
||||
let main maxiter =
|
||||
let open Yojson.Basic.Util in
|
||||
let hand, table = read_json () in
|
||||
let tn = Table.make (table.cards@hand) in
|
||||
let res, _ = Table.alg ~maxiter:14 tn void_printer in
|
||||
let res, _ = Table.alg ~maxiter:maxiter tn void_printer in
|
||||
(* Printf.printf "%a\n" print_table res;; *)
|
||||
to_json res |> Yojson.Basic.to_channel stdout
|
||||
|
||||
let () =
|
||||
let n = Sys.argv.(1) |> int_of_string in
|
||||
main n
|
||||
|
|
|
@ -56,8 +56,9 @@ let neighbors (tcs:tcards) table : card list=
|
|||
|
||||
let constraints start eend =
|
||||
let hand = List.filter (fun ts -> ts.strategy = Single) start.cards in
|
||||
let res = List.filter (fun (e:tcards) -> e.strategy = Single && not (List.mem e hand)) eend.cards in
|
||||
(List.length res) = 0
|
||||
let res = eend.cards |> List.filter (fun (e:tcards) -> e.strategy = Single && not (List.mem e hand)) in
|
||||
let invs = eend.cards |> List.filter (fun (e:tcards) -> e.strategy <> Single && e.tag = Invalid) in
|
||||
(List.length res) = 0 && (List.length invs) = 0
|
||||
|
||||
let doesnt_improve n scores =
|
||||
if List.length scores < n then
|
||||
|
@ -104,7 +105,7 @@ let alg ?maxiter original (dbg: int -> int -> table -> unit) =
|
|||
should_exit := is_best_outcome table;
|
||||
Hashtbl.add set cur_hash ();
|
||||
dbg n cur_score table ;
|
||||
if constraints original table && cur_score > !max_score then
|
||||
if cur_score > !max_score && constraints original table then
|
||||
(max_score := cur_score ; best := table ) ;
|
||||
|
||||
if !should_exit || n > maxiter || doesnt_improve (maxiter/2) uscores then
|
||||
|
|
1
ono_sendai/debug.json
Normal file
1
ono_sendai/debug.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"hand": [["Hearts", 2], ["Tiles", 2], ["Clovers", 3], ["Pikes", 3], ["Hearts", 5], ["Tiles", 5], ["Clovers", 7], ["Hearts", 8], ["Tiles", 8], ["Tiles", 9], ["Tiles", 11], ["Clovers", 12], ["Clovers", 13]], "table": [[["Hearts", 11], ["Hearts", 12], ["Hearts", 13]], [["Clovers", 10], ["Hearts", 10], ["Tiles", 10]]]}
|
|
@ -4,17 +4,19 @@ from picotui.screen import Screen
|
|||
from time import sleep
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
from widgets import *
|
||||
from state import Table, Hand
|
||||
from metro_holografix.cardtypes import *
|
||||
import state
|
||||
|
||||
logging.basicConfig(level=logging.INFO, filename='game.log', filemode='w', format='%(levelname)s - %(message)s')
|
||||
logging.info("START")
|
||||
|
||||
action = ''
|
||||
exit = False
|
||||
ID = "tui"
|
||||
|
||||
game = state.State(["bot1", ID])
|
||||
ID = "you"
|
||||
|
||||
class FrameFactory:
|
||||
titles = ['0x10', '0x11', '0x12', '0x13', '0x14', '0x15', '0x16', '0x17',
|
||||
|
@ -92,7 +94,7 @@ class FrameFactory:
|
|||
def newHandFrame(self, cards):
|
||||
assert type(cards) is list, type(cards)
|
||||
h = 27 # height ?
|
||||
self.d.add(1, 1, WColoredFrame(12, h, 'HAND: '+str(len(cards)), blue))
|
||||
self.d.add(1, 1, WColoredFrame(12, h, 'HAND: '+str(len(cards)-2), blue))
|
||||
coloredCards = [f'{Fore.BLUE}'+cards[0]] + cards[1:-1] + [cards[-1]+f'{Style.RESET_ALL}']
|
||||
w = WCardRadioButton(coloredCards, -1, self.constrainAllWidgets, isHand=True)
|
||||
self.d.add(2, 2, w)
|
||||
|
@ -115,7 +117,7 @@ class FrameFactory:
|
|||
src = i, w.choice-2
|
||||
return src, dst
|
||||
|
||||
def makeButtons(d):
|
||||
def makeButtons(dialog, stats):
|
||||
buttonSend = WColoredButton(7, "SND", C_RED)
|
||||
dialog.add(108, 28, buttonSend)
|
||||
buttonSend.finish_dialog = ACTION_OK
|
||||
|
@ -144,6 +146,9 @@ def makeButtons(d):
|
|||
global action; action = "DRAW"
|
||||
buttonDraw.on_click = btnDraw
|
||||
|
||||
buttonStats = WColoredButton(13, stats, C_BLACK)
|
||||
dialog.add(15, 28, buttonStats)
|
||||
|
||||
buttonAbort = WColoredButton(13, f'{Fore.BLACK}'+" ABRT "+f'{Style.RESET_ALL}', C_WHITE)
|
||||
dialog.add(4, 28, buttonAbort)
|
||||
buttonAbort.finish_dialog = ACTION_OK
|
||||
|
@ -164,7 +169,7 @@ def wrong_play():
|
|||
print(f'{Fore.RED}'+'Wrong play. Retry...'+f'{Style.RESET_ALL}')
|
||||
sleep(2)
|
||||
|
||||
def spawn_and_wait(str):
|
||||
def spawn_and_wait(tstr, difficulty):
|
||||
import sys
|
||||
from subprocess import PIPE, Popen
|
||||
from animation.octopus import animate
|
||||
|
@ -174,8 +179,9 @@ def spawn_and_wait(str):
|
|||
if pid == 0:
|
||||
# child
|
||||
os.close(r)
|
||||
res = Popen(["../hosaka/_build/default/main.exe"], stdout=PIPE, stdin=PIPE)
|
||||
out, err = res.communicate(str.encode('utf-8'))
|
||||
res = Popen(["../hosaka/_build/default/main.exe", str(difficulty)], stdout=PIPE, stdin=PIPE)
|
||||
out, err = res.communicate(tstr.encode('utf-8'))
|
||||
print(out, err)
|
||||
res.stdin.close()
|
||||
w = os.fdopen(w, 'w')
|
||||
w.write(out.decode('utf-8'))
|
||||
|
@ -184,7 +190,7 @@ def spawn_and_wait(str):
|
|||
os.close(w)
|
||||
p = os.waitpid(pid, os.WNOHANG)
|
||||
while p == (0, 0):
|
||||
animate(10)
|
||||
# animate(10)
|
||||
p = os.waitpid(pid, os.WNOHANG)
|
||||
r = os.fdopen(r)
|
||||
output = r.read()
|
||||
|
@ -207,14 +213,16 @@ def validate_auto_play(original, nl):
|
|||
return table, hand
|
||||
|
||||
|
||||
def make_auto_move(original, game):
|
||||
def make_auto_move(original, game, difficulty):
|
||||
table, hand = game.last()
|
||||
tstr = state.toJson(table, hand)
|
||||
output = spawn_and_wait(tstr)
|
||||
output = spawn_and_wait(tstr, difficulty)
|
||||
res = validate_auto_play(original, json.loads(output))
|
||||
if type(res) is str:
|
||||
logging.info(f"BOT-DRAW ({game.nrounds}): {game.cur_player} = {game.last()}")
|
||||
game.draw()
|
||||
elif type(res) is tuple:
|
||||
logging.info(f"BOT-MOVE ({game.nrounds}): {game.cur_player} = {res}")
|
||||
game.advance(*res)
|
||||
game.done()
|
||||
else:
|
||||
|
@ -222,10 +230,16 @@ def make_auto_move(original, game):
|
|||
game.next_turn()
|
||||
|
||||
|
||||
def main(difficulty):
|
||||
global exit, action
|
||||
|
||||
game = state.State(["bot1", ID])
|
||||
|
||||
game.next_turn()
|
||||
|
||||
while not exit and not game.hasEnded:
|
||||
while game.cur_player != ID:
|
||||
make_auto_move(game.last()[0], game)
|
||||
make_auto_move(game.last()[0], game, difficulty)
|
||||
if game.hasEnded == True:
|
||||
break
|
||||
if game.hasEnded == True:
|
||||
|
@ -240,7 +254,11 @@ while not exit and not game.hasEnded:
|
|||
dialog = Dialog(1, 1, 120, 30)
|
||||
f = FrameFactory(dialog)
|
||||
|
||||
makeButtons(dialog)
|
||||
stats = f' Round: {game.nrounds} - '
|
||||
for idp, h in game.players.items():
|
||||
stats += f'{idp}: {len(h.cards)}, '
|
||||
stats = stats[:-2] + ' ' # remove last comma
|
||||
makeButtons(dialog, stats)
|
||||
|
||||
#### FRAMES ####
|
||||
f.emptyFrame()
|
||||
|
@ -255,33 +273,56 @@ while not exit and not game.hasEnded:
|
|||
else:
|
||||
if action == 'EXIT':
|
||||
exit = True
|
||||
elif action == 'MOVE':
|
||||
elif action == 'MOVE' or res == KEY_ENTER or res == b'm':
|
||||
# TODO: transition effect
|
||||
src, dst = f.getChoices()
|
||||
if src[0] is not None and src[1] is not None and dst is not None:
|
||||
game.move_and_advance(src, dst) # get them from next
|
||||
logging.info(f"MOVE ({game.nrounds}): {game.cur_player} = {src}:{dst}")
|
||||
else:
|
||||
continue
|
||||
elif action == 'DRAW':
|
||||
elif action == 'DRAW' or res == b'd':
|
||||
logging.info(f"DRAW ({game.nrounds}): {game.cur_player} = {game.last()}")
|
||||
game.draw()
|
||||
game.next_turn()
|
||||
elif action == 'RESET':
|
||||
while game.size() > 1:
|
||||
game.backtrack()
|
||||
elif action == 'SEND':
|
||||
logging.info(f"RESET ({game.nrounds}): {game.cur_player}")
|
||||
elif action == 'SEND' or res == b's':
|
||||
try:
|
||||
th = game.last()
|
||||
pl = game.cur_player
|
||||
game.done()
|
||||
logging.info(f"DONE ({game.nrounds}): {pl}' = {th}")
|
||||
game.next_turn()
|
||||
except state.WrongMoveException as e:
|
||||
wrong_play()
|
||||
logging.info(f"WRONGPLAY ({game.nrounds}): {game.cur_player} = {game.last()}")
|
||||
elif action == 'BACK':
|
||||
game.backtrack()
|
||||
logging.info(f"BACK ({game.nrounds}): {game.cur_player}")
|
||||
else:
|
||||
assert False
|
||||
pass
|
||||
|
||||
if game.hasEnded:
|
||||
print(f'{Fore.RED}' + "Game has ended, player '" + game.winner + "' has won"+f'{Style.RESET_ALL}')
|
||||
|
||||
print('TODO: ordina per bene KQ12, mostra le carte in mano agli altri')
|
||||
print('LOGGER')
|
||||
print('magari perche` la mossa e` sbagliata')
|
||||
|
||||
if __name__ == '__main__':
|
||||
from sys import argv
|
||||
|
||||
print(argv[1])
|
||||
diff = argv[1]
|
||||
if diff == 'easy':
|
||||
main(7)
|
||||
elif diff == 'medium':
|
||||
main(14)
|
||||
elif diff == 'hard':
|
||||
main(21)
|
||||
else:
|
||||
try:
|
||||
diff = int(diff)
|
||||
except:
|
||||
print('Wrong argument for difficulty')
|
||||
main(int(diff))
|
||||
|
|
|
@ -19,11 +19,12 @@ class Table(Tavolo):
|
|||
|
||||
def widget_repr(self):
|
||||
for ts in self.cards:
|
||||
ocards = list(sorted(ts.cards, key=lambda c: c.value))
|
||||
yi = [sym.big['hat']]
|
||||
seed = ts.cards[0][0].lower()
|
||||
seed = ocards[0][0].lower()
|
||||
yi.append(sym.big[seed])
|
||||
yi.append(sym.big[ts.cards[0][1]])
|
||||
for card in ts.cards[1:]:
|
||||
yi.append(sym.big[ocards[0][1]])
|
||||
for card in ocards[1:]:
|
||||
seed = card[0].lower()
|
||||
yi.append(sym.sym[seed][card[1]])
|
||||
yield yi
|
||||
|
@ -38,14 +39,15 @@ class Table(Tavolo):
|
|||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class Hand:
|
||||
def __init__(self, cards):
|
||||
def sortc(a, b):
|
||||
return -1 if (a[1],a[0]) < (b[1],b[0]) else 1
|
||||
self.cards = list(sorted(cards, key=cmp_to_key(sortc)))
|
||||
|
||||
def __repr__(self):
|
||||
return 'Hand<'+ str(self.cards) + '>'
|
||||
|
||||
def widget_repr(self):
|
||||
yi = [sym.big['hat']]
|
||||
seed = self.cards[0][0].lower()
|
||||
|
@ -61,8 +63,9 @@ def make_deck():
|
|||
def make_set(seed):
|
||||
for i in range(1, 14):
|
||||
yield Card(seed, i)
|
||||
odeck = [m for seed in ['Pikes', 'Hearts', 'Clovers', 'Tiles'] for m in make_set(seed)]
|
||||
odeck = [m for seed in ['Pikes', 'Hearts', 'Clovers', 'Tiles'] for m in make_set(seed)] * 2
|
||||
shuffle(odeck)
|
||||
assert len(odeck) == 104
|
||||
return odeck
|
||||
|
||||
class WrongMoveException(Exception):
|
||||
|
@ -75,9 +78,10 @@ class State:
|
|||
self.hasEnded = False
|
||||
self.players = dict()
|
||||
self.table = Table([])
|
||||
self.round = None
|
||||
self.turn = None
|
||||
self.ids = ids
|
||||
self.cur_player = ids[0]
|
||||
self.nrounds = 0
|
||||
for i in ids:
|
||||
cards = [self.deck.pop() for i in range(11)]
|
||||
self.players[i] = Hand(cards)
|
||||
|
@ -87,48 +91,54 @@ class State:
|
|||
nhand = Hand(hand.cards + [self.deck.pop()])
|
||||
self.players[self.cur_player] = nhand
|
||||
assert len(self.players[self.cur_player].cards) == len(hand.cards) + 1
|
||||
self.round = None
|
||||
self.turn = None
|
||||
|
||||
def next_turn(self):
|
||||
assert self.round is None
|
||||
assert self.turn is None
|
||||
next_player = self.ids[(self.ids.index(self.cur_player) + 1) % len(self.ids)]
|
||||
self.cur_player = next_player
|
||||
self.round = [(copy(self.table), copy(self.players[self.cur_player]))]
|
||||
self.turn = [(copy(self.table), copy(self.players[self.cur_player]))]
|
||||
self.nrounds = self.nrounds + 1 if self.cur_player == self.ids[0] else self.nrounds
|
||||
|
||||
def done(self):
|
||||
assert self.round is not None
|
||||
assert self.turn is not None
|
||||
original = self.table
|
||||
table, hand = self.last()
|
||||
if not table.is_valid() or len(set(original.flatten()) - set(table.flatten())) != 0:
|
||||
|
||||
if original.equality(table):
|
||||
raise WrongMoveException()
|
||||
elif not table.is_valid() or len(set(original.flatten()) - set(table.flatten())) != 0:
|
||||
if self.cur_player != 'you':
|
||||
fuck() # debug
|
||||
raise WrongMoveException()
|
||||
else:
|
||||
self.table, self.players[self.cur_player] = table, hand
|
||||
self.round = None
|
||||
self.turn = None
|
||||
if len(hand.cards) == 0:
|
||||
# won
|
||||
self.hasEnded = True
|
||||
self.winner = self.cur_player
|
||||
|
||||
def last(self):
|
||||
return self.round[-1]
|
||||
return self.turn[-1]
|
||||
|
||||
def backtrack(self):
|
||||
if len(self.round) >= 2:
|
||||
return self.round.pop()
|
||||
if len(self.turn) >= 2:
|
||||
return self.turn.pop()
|
||||
else:
|
||||
return self.round[0]
|
||||
return self.turn[0]
|
||||
|
||||
def size(self):
|
||||
return len(self.round)
|
||||
return len(self.turn)
|
||||
|
||||
def move_and_advance(self, src, dst):
|
||||
table, hand = self.last()
|
||||
t, h = gioca(table, hand, src, dst)
|
||||
self.round.append((t, h))
|
||||
self.turn.append((t, h))
|
||||
return t, h
|
||||
|
||||
def advance(self, table, hand):
|
||||
self.round.append((table, hand))
|
||||
self.turn.append((table, hand))
|
||||
return table, hand
|
||||
|
||||
def fromJson(j):
|
||||
|
@ -146,12 +156,12 @@ def toJson(table, hand):
|
|||
j['table'].append(tc.cards)
|
||||
return json.dumps(j)
|
||||
|
||||
|
||||
# TODO: refactor language
|
||||
def gioca(tavolo, hand, src, dst):
|
||||
giocata = [] if dst == 'Empty' else tavolo.cards[dst]
|
||||
da_muovere = hand.cards[src[1]] if src[0] == 'Hand' else tavolo.cards[src[0]].cards[src[1]]
|
||||
hcards = hand.cards[:src[1]] + hand.cards[src[1]+1:] if src[0] == 'Hand' else hand.cards
|
||||
assert src[0] != 'Hand' or len(hcards) == len(hand.cards) - 1
|
||||
|
||||
assert type(dst) is int or dst == 'Empty'
|
||||
assert type(src[0]) is int or src[0] == 'Hand'
|
||||
|
@ -173,4 +183,3 @@ def gioca(tavolo, hand, src, dst):
|
|||
else:
|
||||
news.append(t)
|
||||
return Table(news), Hand(hcards)
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ class WColoredButton(WButton):
|
|||
return r
|
||||
|
||||
def handle_key(self, inp):
|
||||
if inp in [KEY_ENTER, b's', b'd', b'm']:
|
||||
return inp
|
||||
pass
|
||||
|
||||
def on_click(self, *args, **kwargs):
|
||||
|
@ -42,7 +44,10 @@ class WColoredFrame(WFrame):
|
|||
title = color.decode('utf-8') + title + self.rst.decode('utf-8')
|
||||
super().__init__(w, h, title)
|
||||
self.color = color
|
||||
|
||||
def handle_key(self, inp):
|
||||
if inp in [KEY_ENTER, b's', b'd', b'm']:
|
||||
return inp
|
||||
pass
|
||||
|
||||
def draw_box(self, left, top, width, height):
|
||||
|
@ -86,7 +91,9 @@ class WCardRadioButton(WRadioButton):
|
|||
self.id = id
|
||||
|
||||
def handle_key(self, inp):
|
||||
pass # TODO: maybe enable keyboard
|
||||
if inp in [KEY_ENTER, b's', b'd', b'm']:
|
||||
return inp
|
||||
pass
|
||||
|
||||
def redraw(self):
|
||||
i = 0
|
||||
|
|
Loading…
Reference in a new issue