245 lines
10 KiB
Python
245 lines
10 KiB
Python
import copy, random
|
|
from cards import *
|
|
|
|
def yes_no(q, default=None):
|
|
while True:
|
|
a=input(q+' (j/n): ').strip().lower()
|
|
if a in ('j','ja','y','yes'): return True
|
|
if a in ('n','nein','no'): return False
|
|
if not a and default is not None: return default
|
|
print(' Bitte j oder n eingeben.')
|
|
|
|
def number(q, lo, hi):
|
|
while True:
|
|
a=input(q+f' ({lo}-{hi}): ').strip()
|
|
try: n=int(a)
|
|
except ValueError:
|
|
print(f' Bitte eine Zahl von {lo} bis {hi} eingeben.'); continue
|
|
if lo<=n<=hi: return n
|
|
print(f' Bitte eine Zahl von {lo} bis {hi} eingeben.')
|
|
|
|
class Player:
|
|
def __init__(self,name,bot=False):
|
|
self.name=name
|
|
self.bot=bot
|
|
self.hand=[]
|
|
self.opened=False
|
|
|
|
class Game:
|
|
def __init__(self):
|
|
print('EINSTELLUNGEN')
|
|
print(' ~~~~~~~~~~~~')
|
|
n=number(' Wie viele menschliche Spieler', 1, 4)
|
|
if 4-n>0:
|
|
b=number(' Wie viele Bots' + (' (mindestens 1)' if n==1 else ' (0 für keine)'), 1 if n==1 else 0, 4-n)
|
|
else:
|
|
b=0
|
|
self.wrap=yes_no(' 13-1-Umlauf erlauben (z.B. 12,13,1)')
|
|
self.min30=yes_no(' Braucht die Erstmeldung 30 Punkte')
|
|
self.players=[Player(f'Spieler {i+1}') for i in range(n)]+[Player(f'Bot {i+1}', bot=True) for i in range(b)]
|
|
self.turn=0
|
|
self.table=[]
|
|
self.deck=deck(); random.shuffle(self.deck)
|
|
self.history=[]
|
|
self.drew=False
|
|
self.melded=False
|
|
self.handover=False
|
|
for p in self.players:
|
|
p.hand=sort_hand([self.deck.pop() for _ in range(14)])
|
|
|
|
@property
|
|
def p(self): return self.players[self.turn]
|
|
|
|
def save(self):
|
|
self.history.append(copy.deepcopy((self.players,self.table,self.deck,self.drew,self.melded)))
|
|
|
|
def undo(self):
|
|
if not self.history:
|
|
print('Nichts zum Rückgängigmachen.'); return
|
|
self.players,self.table,self.deck,self.drew,self.melded=self.history.pop()
|
|
|
|
def clear(self): print('\033[2J\033[H',end='')
|
|
|
|
def show(self):
|
|
self.clear()
|
|
print('╔════════════════════════════════════════════════════╗')
|
|
print('║ MYRUMMY ║')
|
|
print('╚════════════════════════════════════════════════════╝')
|
|
print(f' Am Zug: {self.p.name} Karten im Stapel: {len(self.deck)}')
|
|
print(f' Regeln: 13-1-Umlauf: {"ja" if self.wrap else "nein"} Erstmeldung 30: {"ja" if self.min30 else "nein"}')
|
|
print(f' Erstmeldung: {"ja" if self.p.opened else "nein"}')
|
|
print('\nTISCH')
|
|
if not self.table: print(' (leer)')
|
|
for i,g in enumerate(self.table,1):
|
|
print(f' {i:>2}: ' + ' '.join(f'{j}:{c.text()}' for j,c in enumerate(g,1)) + f' [{kind(g, self.wrap)}]')
|
|
print('\nHAND')
|
|
lines=[]; key=None
|
|
for i,c in enumerate(self.p.hand,1):
|
|
k='J' if c.joker else c.color
|
|
if k!=key: lines.append([]); key=k
|
|
lines[-1].append(f'{i}:{c.text()}')
|
|
for l in lines: print(' '+ ' '.join(l))
|
|
print('\nBEFEHLE')
|
|
print(' m H G Handkarte H -> Gruppe G')
|
|
print(' m H new Handkarte H -> neue Gruppe')
|
|
print(' t G C Tischkarte C aus Gruppe G -> Hand')
|
|
print(' n leere neue Gruppe')
|
|
print(' a G1 G2 Gruppen verbinden')
|
|
print(' s G POS Gruppe nach POS teilen')
|
|
print(' d ziehen (beendet den Zug)')
|
|
print(' u undo')
|
|
print(' sort Hand sortieren')
|
|
print(' finish Zug prüfen/beenden')
|
|
print(' q beenden')
|
|
|
|
def idx(self,x,maxi):
|
|
n=int(x)
|
|
if not 1<=n<=maxi: raise ValueError('Nummer außerhalb des gültigen Bereichs.')
|
|
return n-1
|
|
|
|
def move_hand(self,h,g):
|
|
hi=self.idx(h,len(self.p.hand)); self.save()
|
|
card=self.p.hand.pop(hi)
|
|
if g.lower()=='new': self.table.append([card])
|
|
else: self.table[self.idx(g,len(self.table))].append(card)
|
|
self.melded=True
|
|
|
|
def move_table(self,g,c):
|
|
gi=self.idx(g,len(self.table)); ci=self.idx(c,len(self.table[gi])); self.save()
|
|
self.p.hand.append(self.table[gi].pop(ci))
|
|
if not self.table[gi]: self.table.pop(gi)
|
|
self.p.hand=sort_hand(self.p.hand)
|
|
|
|
def merge(self,a,b):
|
|
ai=self.idx(a,len(self.table)); bi=self.idx(b,len(self.table))
|
|
if ai==bi: raise ValueError('Gleiche Gruppe.')
|
|
self.save(); self.table[ai]+=self.table[bi]; self.table.pop(bi)
|
|
|
|
def split(self,g,pos):
|
|
gi=self.idx(g,len(self.table)); pos=int(pos)
|
|
if not 1<=pos<len(self.table[gi]): raise ValueError('Teilposition ungültig.')
|
|
self.save(); x=self.table[gi]; self.table[gi]=x[:pos]; self.table.insert(gi+1,x[pos:])
|
|
|
|
def draw(self):
|
|
if self.melded: raise ValueError('Du hast bereits Karten gelegt - ziehen ist dann nicht mehr erlaubt.')
|
|
if not self.deck: raise ValueError('Stapel leer.')
|
|
card=self.deck.pop(); self.p.hand.append(card); self.p.hand=sort_hand(self.p.hand)
|
|
print(f'\n {self.p.name} zieht {card.text()} und beendet damit den Zug.')
|
|
self.turn=(self.turn+1)%len(self.players); self.drew=False; self.melded=False; self.history=[]
|
|
self.handover=True
|
|
|
|
def finish(self):
|
|
if any(not g or not valid(g, self.wrap) for g in self.table):
|
|
print('Zug ungültig: Es gibt eine ungültige Tischgruppe.')
|
|
return False
|
|
if self.table:
|
|
if not self.p.opened:
|
|
if self.min30:
|
|
total=sum(sum(points(c) for c in g) for g in self.table)
|
|
if total<30:
|
|
print(f'Erstmeldung braucht mindestens 30 Punkte. Aktuell: {total}')
|
|
return False
|
|
self.p.opened=True
|
|
elif not self.drew:
|
|
print('Du musst entweder Karten legen oder ziehen.')
|
|
return False
|
|
if not self.p.hand:
|
|
print(f'\n{self.p.name} gewinnt!')
|
|
return True
|
|
self.turn=(self.turn+1)%len(self.players); self.drew=False; self.melded=False; self.history=[]
|
|
self.handover=True
|
|
return False
|
|
|
|
def command(self,line):
|
|
a=line.split()
|
|
if not a:return False
|
|
try:
|
|
cmd=a[0].lower()
|
|
if cmd=='m' and len(a)==3:self.move_hand(a[1],a[2])
|
|
elif cmd=='t' and len(a)==3:self.move_table(a[1],a[2])
|
|
elif cmd=='n':self.save();self.table.append([])
|
|
elif cmd=='a' and len(a)==3:self.merge(a[1],a[2])
|
|
elif cmd=='s' and len(a)==3:self.split(a[1],a[2])
|
|
elif cmd=='d':self.draw()
|
|
elif cmd=='u':self.undo()
|
|
elif cmd=='sort':self.p.hand=sort_hand(self.p.hand)
|
|
elif cmd=='finish':
|
|
before=self.turn
|
|
r=self.finish()
|
|
if not r and self.turn==before: input('\nENTER...')
|
|
return r
|
|
elif cmd in ('q','quit','exit'):raise SystemExit
|
|
elif cmd=='show':pass
|
|
else: raise ValueError('Unbekannter Befehl.')
|
|
except (ValueError,IndexError) as e: print('Fehler:',e)
|
|
input('\nENTER...')
|
|
return False
|
|
|
|
def find_meld(self, hand):
|
|
# Satz: gleicher Wert, unterschiedliche Farben, 3-4 Karten
|
|
by_val={}
|
|
for c in hand:
|
|
if not c.joker: by_val.setdefault(c.value, []).append(c)
|
|
for val in sorted(by_val):
|
|
by_col={}
|
|
for c in by_val[val]: by_col.setdefault(c.color, []).append(c)
|
|
if len(by_col)>=3:
|
|
return [by_col[col][0] for col in sorted(by_col)[:4]]
|
|
# Folge: gleiche Farbe, aufeinanderfolgend, 3+ Karten
|
|
by_col={}
|
|
for c in hand:
|
|
if not c.joker: by_col.setdefault(c.color, []).append(c)
|
|
for col in by_col:
|
|
vals=sorted({c.value for c in by_col[col]})
|
|
i=0
|
|
while i<len(vals):
|
|
j=i
|
|
while j+1<len(vals) and vals[j+1]==vals[j]+1: j+=1
|
|
if j-i+1>=3:
|
|
return [next(c for c in by_col[col] if c.value==v) for v in vals[i:j+1]]
|
|
i=j+1
|
|
return None
|
|
|
|
def bot_turn(self):
|
|
p=self.p
|
|
melded=[]
|
|
while True:
|
|
m=self.find_meld(p.hand)
|
|
if not m: break
|
|
for c in m: p.hand.remove(c)
|
|
melded.append(m)
|
|
total=sum(points(c) for m in melded for c in m)
|
|
if melded and (p.opened or not self.min30 or total>=30):
|
|
for m in melded: self.table.append(m)
|
|
if not p.opened: p.opened=True
|
|
if not p.hand:
|
|
print(f'\n {p.name} gewinnt!')
|
|
return True
|
|
else:
|
|
for m in melded:
|
|
for c in m: p.hand.append(c)
|
|
p.hand=sort_hand(p.hand)
|
|
if self.deck:
|
|
p.hand.append(self.deck.pop()); p.hand=sort_hand(p.hand)
|
|
self.turn=(self.turn+1)%len(self.players); self.drew=False; self.melded=False; self.history=[]
|
|
self.handover=True
|
|
return False
|
|
|
|
def run(self):
|
|
while True:
|
|
if self.p.bot:
|
|
if self.bot_turn(): return
|
|
else:
|
|
self.show()
|
|
if self.command(input('\n> ')): return
|
|
if self.handover:
|
|
self.handover=False
|
|
self.clear()
|
|
if self.p.bot:
|
|
print(f'\n Zug beendet. {self.p.name} ist jetzt am Zug,')
|
|
print(' seine Karten bleiben verborgen.')
|
|
else:
|
|
print(f'\n Zug beendet. Bitte gib den Computer an {self.p.name} weiter,')
|
|
print(' damit der andere Spieler deine Karten nicht sehen kann.')
|
|
input(' ENTER drücken, wenn du bereit bist...')
|