diff --git a/metro_holografix/cardtypes.py b/metro_holografix/cardtypes.py index 40bd55b..44a2eed 100644 --- a/metro_holografix/cardtypes.py +++ b/metro_holografix/cardtypes.py @@ -32,7 +32,7 @@ class TaggedCards: return self.cards.__iter__() def __eq__(self, other): - assert type(other) is type(self) + assert type(other) is type(self), type(other) if len(other.cards) != len(self.cards) or self.tag != other.tag or self.tipo != other.tipo: return False else: @@ -53,7 +53,6 @@ class TaggedCards: return False class Tavolo: - cards = list() # lista di taggedcards def __init__(self, cs): assert type(cs) is list self.cards = cs @@ -83,6 +82,9 @@ class Tavolo: def punteggio(self): return len(self.getValide()) - len(self.getNonValide()) + def singles(self): + return [c for c in self.cards if c.tipo == 'Singolo'] + # old ocaml work def flatten(lst): return sorted([s for subl in lst for s in subl], key=lambda x: x.value) diff --git a/metro_holografix/scrapes/cardtypes.py b/metro_holografix/scrapes/cardtypes.py new file mode 100644 index 0000000..40bd55b --- /dev/null +++ b/metro_holografix/scrapes/cardtypes.py @@ -0,0 +1,156 @@ +from collections import namedtuple; + +Card = namedtuple('Card', 'seed value') + +class Mano: + cards = list() # lista di cards + def __init__(self, carte): + assert type(carte) is list and type(carte[0]) is Card + self.cards = carte # lista di Carte + +class TaggedCards: + cards = None + tag = '' + tipo = '' + def __init__(self, carte): + assert type(carte) is list and type(carte[0]) is Card + self.cards = list(sorted(carte, key=lambda x: str(x))) # lista di Carte + self.tag = 'NonValido' if not is_valida(self.cards) else 'Valido' + self.tipo = 'Singolo' if len(carte) == 1 else 'Tris' if is_tris(carte) else 'Scala' + + def __hash__(self): + import functools + def cmp(c1, c2): + return c1.value < c2.value if c1.seed == c2.seed else c1.seed < c2.seed + lst = tuple(sorted(self.cards, key=functools.cmp_to_key(cmp))) + return hash(lst) + + def __repr__(self): + return "TaggedCards<%s, %s, %s>" % (self.cards, self.tag, self.tipo) + + def __iter__(self): + return self.cards.__iter__() + + def __eq__(self, other): + assert type(other) is type(self) + if len(other.cards) != len(self.cards) or self.tag != other.tag or self.tipo != other.tipo: + return False + else: + return set(self.cards) == set(other.cards) + + def __gt__(self, other): + if self.tipo == 'Tris' and len(self.cards) == 4: + return False + if other.tipo == 'Tris' and len(other.cards) == 4: + return True + elif self.tipo != 'Singolo' and other.tipo == 'Singolo': + return True + elif self.tipo == 'Singolo' and other.tipo != 'Singolo': + return False + elif self.tag == 'NonValido' and other.tag == 'Valido': + return True + else: + return False + +class Tavolo: + cards = list() # lista di taggedcards + def __init__(self, cs): + assert type(cs) is list + self.cards = cs + + def __hash__(self): + return sum([c.__hash__() for c in self.cards]) + + def __repr__(self): + return "Tavolo<%s>" % (self.cards,) + + def getNonValide(self): + assert type(self.cards[0]) is TaggedCards + f = [c for c in self.cards if c.tag == 'NonValido'] + return f + + def getValide(self): + assert type(self.cards[0]) is TaggedCards + f = [c for c in self.cards if c.tag == 'Valido'] + return f + + def getAll(self): + return self.cards + + def llen(self): + return len(flatten(self.getAll())) + + def punteggio(self): + return len(self.getValide()) - len(self.getNonValide()) + +# old ocaml work +def flatten(lst): + return sorted([s for subl in lst for s in subl], key=lambda x: x.value) + +def no_double_seed(carte): + seeds = set([c.seed for c in carte]) + return len(seeds) == len(carte) + +def is_only_one_seed(carte): + seeds = set([c.seed for c in carte]) + return len(seeds) == 1 + +def no_double_value(carte): + seeds = set([c.value for c in carte]) + return len(seeds) == len(carte) + +def is_tris(carte): + values = set([c.value for c in carte]) + if len(values) == 1: + return no_double_seed(carte) + else: + return False + +def split(values, accum=[]): + a = values[0] + if len(values) > 2: + b = values[1] + if a == b - 1: + return split(values[1:], accum + [a]) + else: + return accum + [a], values[1:] + else: + return accum + [a], values[1:] + +def is_straight(carte): + assert type(carte) is list and type(carte[0]) is Card + def _is_straight(carte): + if len(carte) == 1: + return True + elif len(carte) == 0: + assert False + else: + a = carte[0] + b = carte[1] + if a == b - 1: + return _is_straight(carte[1:]) + else: + return False + + if not (no_double_value(carte) and is_only_one_seed(carte)): + return False + else: + values = [v for s, v in sorted(carte, key=lambda x:x.value)] + first, last = values[0], values[-1] + if last == 13 and first == 1: + head, tail = split(values) + return _is_straight(head) and _is_straight(tail) + else: + return _is_straight(values) + +def is_valida(carte): + carte = list(sorted(carte, key = lambda x : x[1])) + if len(carte) < 3: + return False + else: + a = carte[0][1] + b = carte[1][1] + if a == b: + return is_tris(carte) + else: + return is_straight(carte) diff --git a/ono_sendai/animation/0.txt b/ono_sendai/animation/0.txt index 57414c0..82698f9 100644 --- a/ono_sendai/animation/0.txt +++ b/ono_sendai/animation/0.txt @@ -20,3 +20,4 @@ +LOADING... diff --git a/ono_sendai/animation/1.txt b/ono_sendai/animation/1.txt index c2bea6c..407f903 100644 --- a/ono_sendai/animation/1.txt +++ b/ono_sendai/animation/1.txt @@ -20,3 +20,4 @@ Φ╚╫╫╫╫` ▀╬╫N╨▌ ▒╩╫╫╫▌ ▀╫╫╧* +LOADING... diff --git a/ono_sendai/animation/2.txt b/ono_sendai/animation/2.txt index 1236383..ee986f5 100644 --- a/ono_sendai/animation/2.txt +++ b/ono_sendai/animation/2.txt @@ -20,3 +20,4 @@ "╩Ñ╬▌ ╙ÑÑ╨` +LOADING... diff --git a/ono_sendai/animation/3.txt b/ono_sendai/animation/3.txt index a4607d2..462f18b 100644 --- a/ono_sendai/animation/3.txt +++ b/ono_sendai/animation/3.txt @@ -19,4 +19,5 @@ ▀B╨╜╜≡▄▄╠╩╝╨╨"` ,H╫╫╫╫╫▀ ╙▒╫Ñ╫╩▌""╨╨╩╩▄▄╗≡╜╜VQ▀ ` .╬]╫╫╫╫╛ ╙▓╫╫N╨╫⌐ ` "╩Ñ╣▀ ╙ÑÑ" - + +LOADING... diff --git a/ono_sendai/animation/4.txt b/ono_sendai/animation/4.txt index da8cc4b..2844d0a 100644 --- a/ono_sendai/animation/4.txt +++ b/ono_sendai/animation/4.txt @@ -20,3 +20,4 @@ '╝╨'"╜▀ßM^` ▒╨╫╫╫╫⌐ ▀╬╫Ñ╙╫⌐ └╝╧╙└` ╜┘ "╩ÑÑ╩ ▀ÑÑ"` +LOADING... diff --git a/ono_sendai/animation/__pycache__/octopus.cpython-36.pyc b/ono_sendai/animation/__pycache__/octopus.cpython-36.pyc new file mode 100644 index 0000000..fe60c53 Binary files /dev/null and b/ono_sendai/animation/__pycache__/octopus.cpython-36.pyc differ diff --git a/ono_sendai/animation/prova.py b/ono_sendai/animation/octopus.py similarity index 84% rename from ono_sendai/animation/prova.py rename to ono_sendai/animation/octopus.py index b77f64b..a2db475 100644 --- a/ono_sendai/animation/prova.py +++ b/ono_sendai/animation/octopus.py @@ -7,11 +7,12 @@ from os import system frames = [] for i in range(5): - with open(str(i)+'.txt', 'r') as f: + with open('animation/'+str(i)+'.txt', 'r') as f: content = f.read() frames.append(f'{Fore.MAGENTA}'+content+f'{Style.RESET_ALL}') def animate(n): + system('clear') for i in range(n): print(frames[i%5]) sleep(0.2) diff --git a/ono_sendai/broker.py b/ono_sendai/broker.py deleted file mode 100644 index 217962f..0000000 --- a/ono_sendai/broker.py +++ /dev/null @@ -1,4 +0,0 @@ -from colorama import Style,Fore -from symbols import * - -print(big['hat'] + n + big['clovers'] + n + big[1] + n + clovers[2] + n + clovers[3] + n) diff --git a/ono_sendai/cards b/ono_sendai/cards deleted file mode 100644 index 2582c97..0000000 --- a/ono_sendai/cards +++ /dev/null @@ -1,25 +0,0 @@ - ___ -❘♠ ❘ -❘ ̲̲ ̲2̲❘ -❘♠̲̲ ̲1̲❘ -❘♠̲̲ ̲K̲❘ - ___ -❘♦ ❘ -❘ ̲̲ ̲3̲❘ -❘♦̲̲ ̲4̲❘ -❘♦̲̲ ̲5̲❘ -❘♦̲̲ ̲6̲❘ -❘♦̲̲ ̲7̲❘ -❘♦̲̲ ̲8̲❘ -❘♦̲̲ ̲9̲❘ -❘♦̲̲1̲0̲❘ - ___ -❘♥ ❘ -❘ ̲̲ ̲8̲❘ -❘♥̲̲ ̲9̲❘ -❘♥̲̲ ̲Q̲❘ - ___ -❘♣ ❘ -❘ ̲̲ ̲4̲❘ -❘♣̲̲ ̲5̲❘ -❘♣̲̲ ̲6̲❘ \ No newline at end of file diff --git a/ono_sendai/demo.py b/ono_sendai/demo.py new file mode 100644 index 0000000..cb29b5b --- /dev/null +++ b/ono_sendai/demo.py @@ -0,0 +1,23 @@ + + self.events = [ # list of tuples table-hand +# (Table([ +# TaggedCards([ +# Card("Pikes", 2), +# Card("Clovers", 2), +# Card("Tiles", 2), +# Card("Hearts", 2)]), +# TaggedCards([ +# Card("Hearts", 1), +# Card("Clovers", 1), +# Card("Pikes", 1), +# Card("Tiles", 1)]), +# ]), Hand([ +# Card("Pikes", 12), +# Card("Clovers", 12), +# Card("Tiles", 12), +# Card("Hearts", 12), +# Card("Hearts", 13), +# Card("Clovers", 13), +# Card("Pikes", 13), +# Card("Tiles", 13) +# ])) diff --git a/ono_sendai/prova.py b/ono_sendai/prova.py index a187777..ac6f6b8 100644 --- a/ono_sendai/prova.py +++ b/ono_sendai/prova.py @@ -2,12 +2,16 @@ from picotui.context import Context from picotui.screen import Screen from time import sleep +import os from widgets import * import state action = '' exit = False +ID = "tui" + +game = state.State([ID, "bot1"]) class FrameFactory: titles = ['0x10', '0x11', '0x12', '0x13', '0x14', '0x15', '0x16', '0x17', @@ -85,7 +89,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', blue)) + self.d.add(1, 1, WColoredFrame(12, h, 'HAND: '+str(len(cards)), 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) @@ -151,9 +155,36 @@ def makeButtons(d): global action ; action = "BACK" buttonback.on_click = doBack +def wrong_play(): + from sys import stdout + os.system('clear') + print(f'{Fore.RED}'+'Wrong play. Retry...'+f'{Style.RESET_ALL}') + sleep(2) +def make_auto_move(): + import sys + from animation.octopus import animate + pid = os.fork() + if pid == 0: + # child + os.system('sleep 1') + os.system('echo DRAW') + sys.exit() + else: + p = os.waitpid(pid, os.WNOHANG) + while p == (0, 0): + animate(10) + p = os.waitpid(pid, os.WNOHANG) + game.draw() + game.next_turn() + + +game.next_turn() while not exit: - table, hand = state.next() + while game.cur_player != ID: + make_auto_move() + + table, hand = game.last() with Context(): Screen.attr_color(C_WHITE, C_GREEN) @@ -179,15 +210,20 @@ while not exit: exit = True elif action == 'MOVE': # TODO: transition effect - state.update_table(table, hand, *f.getChoices()) # get them from next + game.advance(*f.getChoices()) # get them from next elif action == 'DRAW': - pass + game.draw() + game.next_turn() elif action == 'RESET': - while state.size() > 1: - state.prev() + while game.size() > 1: + game.backtrack() elif action == 'SEND': - pass + try: + game.done() + game.next_turn() + except state.WrongMoveException as e: + wrong_play() elif action == 'BACK': - state.prev() + game.backtrack() else: assert False diff --git a/ono_sendai/state.py b/ono_sendai/state.py index 3cb440e..b74585a 100644 --- a/ono_sendai/state.py +++ b/ono_sendai/state.py @@ -1,15 +1,18 @@ import sys sys.path.append('../') import json +from copy import deepcopy as copy +from functools import cmp_to_key + +from IPython import embed as fuck from metro_holografix.cardtypes import * import symbols as sym -Hand = Tavolo class Table(Tavolo): def is_valid(self): - return len(self.getNonValide()) == 0 + return len(self.singles()) == 0 and len(self.getNonValide()) == 0 def widget_repr(self): for ts in self.cards: @@ -25,7 +28,9 @@ class Table(Tavolo): class Hand: def __init__(self, cards): - self.cards = 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 widget_repr(self): yi = [sym.big['hat']] @@ -36,31 +41,70 @@ class Hand: seed = card[0].lower() yi.append(sym.sym[seed][card[1]]) return yi - -events = [ # list of tuples table-hand - (Table([ - TaggedCards([ - Card("Pikes", 2), - Card("Clovers", 2), - Card("Tiles", 2), - Card("Hearts", 2)]), - TaggedCards([ - Card("Hearts", 1), - Card("Clovers", 1), - Card("Pikes", 1), - Card("Tiles", 1)]), - ]), Hand([ - Card("Pikes", 12), - Card("Clovers", 12), - Card("Tiles", 12), - Card("Hearts", 12), - Card("Hearts", 13), - Card("Clovers", 13), - Card("Pikes", 13), - Card("Tiles", 13) - ])) -] +def make_deck(): + from random import shuffle # TODO: mersenne + 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)] + shuffle(odeck) + return odeck + +class WrongMoveException(Exception): + pass + +class State: + def __init__(self, ids): + self.deck = make_deck() + self.players = dict() + self.table = Table([]) + self.round = None + self.ids = ids + self.cur_player = ids[0] + for i in ids: + cards = [self.deck.pop() for i in range(11)] + self.players[i] = Hand(cards) + + def draw(self): + hand = self.players[self.cur_player] + nhand = Hand(hand.cards + [self.deck.pop()]) + self.players[self.cur_player] = nhand + self.round = None + + def next_turn(self): + assert self.round 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]))] + + def done(self): + assert self.round is not None + original = self.table + table, hand = self.last() + if not table.is_valid() or len(set(original.cards) - set(table.cards)) != 0: + raise WrongMoveException() + else: + self.table, self.players[self.cur_player] = table, hand + self.round = None + + def last(self): + return self.round[-1] + + def backtrack(self): + if len(self.round) >= 2: + return self.round.pop() + else: + return self.round[0] + + def size(self): + return len(self.round) + + def advance(self, src, dst): + table, hand = self.last() + t, h = gioca(table, hand, src, dst) + self.round.append((t, h)) + return t, h def fromJson(j): hcards = [Card(seed, value) for seed, value in j['hand']] @@ -77,17 +121,6 @@ def toJson(table, hand): j['table'].append(tc.cards) return json.dumps(j) -def next(): - return events[-1] - -def prev(): - if len(events) >= 2: - return events.pop() - else: - return events[0] - -def size(): - return len(events) # TODO: refactor language def gioca(tavolo, hand, src, dst): @@ -116,7 +149,3 @@ def gioca(tavolo, hand, src, dst): news.append(t) return Table(news), Hand(hcards) -def update_table(table, hand, src, dst): - t, h = gioca(table, hand, src, dst) - events.append((t, h)) - return t, h diff --git a/ono_sendai/symbols.py b/ono_sendai/symbols.py index 1b0f5c7..8b52dc1 100644 --- a/ono_sendai/symbols.py +++ b/ono_sendai/symbols.py @@ -7,7 +7,7 @@ pikes = { 6: '❘♠̲̲ ̲6̲❘', 7: '❘♠̲̲ ̲7̲❘', 8: '❘♠̲̲ ̲8̲❘', - 8: '❘♠̲̲ ̲9̲❘', + 9: '❘♠̲̲ ̲9̲❘', 10: '❘♠̲̲1̲0̲❘', 11: '❘♠̲̲ ̲J̲❘', 12: '❘♠̲̲ ̲Q̲❘', @@ -23,7 +23,7 @@ clovers = { 6: '❘♣̲̲ ̲6̲❘', 7: '❘♣̲̲ ̲7̲❘', 8: '❘♣̲̲ ̲8̲❘', - 8: '❘♣̲̲ ̲9̲❘', + 9: '❘♣̲̲ ̲9̲❘', 10: '❘♣̲̲1̲0̲❘', 11: '❘♣̲̲ ̲J̲❘', 12: '❘♣̲̲ ̲Q̲❘', @@ -39,7 +39,7 @@ hearts = { 6: '❘♥̲̲ ̲6̲❘', 7: '❘♥̲̲ ̲7̲❘', 8: '❘♥̲̲ ̲8̲❘', - 8: '❘♥̲̲ ̲9̲❘', + 9: '❘♥̲̲ ̲9̲❘', 10: '❘♥̲̲1̲0̲❘', 11: '❘♥̲̲ ̲J̲❘', 12: '❘♥̲̲ ̲Q̲❘', @@ -55,7 +55,7 @@ tiles = { 6: '❘♦̲̲ ̲6̲❘', 7: '❘♦̲̲ ̲7̲❘', 8: '❘♦̲̲ ̲8̲❘', - 8: '❘♦̲̲ ̲9̲❘', + 9: '❘♦̲̲ ̲9̲❘', 10: '❘♦̲̲1̲0̲❘', 11: '❘♦̲̲ ̲J̲❘', 12: '❘♦̲̲ ̲Q̲❘',