""" TWEE RUNS op dezelfde bied-constraints. Constraints (wat Zuid via het bieden weet): West : 5+ harten, regel van 20, >=11 HCP (opent 1H) Oost : 6+ HCP, 4+ schoppen, 0-2 harten (biedt 1S) Zuid : exact de 22-puntershand Noord: rest (vrij) RUN A (dia 6): over de VOLLEDIGE populatie -> hoe vaak kan NZ NERGENS makend heen (= passen objectief best). RUN B: zoek waterdichte haakspellen (OW-manche EN NZ nergens makend) als illustratie. Zelfde filter, maar nu printen we de deals. """ import random from collections import Counter from endplay.types import Deal, Player, Denom from endplay.dds import calc_dd_table, par SUITS=["S","H","D","C"]; RANKS="AKQJT98765432" HCP={"A":4,"K":3,"Q":2,"J":1} ZUID=[("S","3"),("S","2"),("H","K"),("H","J"),("H","2"), ("D","A"),("D","K"),("D","Q"),("D","T"), ("C","A"),("C","K"),("C","Q"),("C","T")] def deck(): return [(s,r) for s in SUITS for r in RANKS] def hcp(c): return sum(HCP.get(r,0) for (_,r) in c) def ln(c,s): return sum(1 for (x,_) in c if x==s) def r20(c): L=sorted([ln(c,s) for s in SUITS],reverse=True); return hcp(c)+L[0]+L[1]>=20 def west_ok(c): return ln(c,"H")>=5 and hcp(c)>=11 and r20(c) def oost_ok(c): return hcp(c)>=6 and ln(c,"S")>=4 and ln(c,"H")<=2 # 0-2 harten def to_pbn(h): o={r:i for i,r in enumerate(RANKS)} def hs(cards): b={s:"" for s in SUITS} for (s,r) in cards: b[s]+=r return ".".join("".join(sorted(b[s],key=lambda x:o[x])) for s in SUITS) return "N:"+" ".join(hs(h[p]) for p in ["N","E","S","W"]) def gen(rng): zset=set(ZUID); rest=[c for c in deck() if c not in zset] while True: rng.shuffle(rest) W=rest[0:13] if not west_ok(W): continue E=rest[13:26] if not oost_ok(E): continue return {"N":rest[26:39],"E":E,"S":list(ZUID),"W":W} def mx(t,denom,side): return max(t[denom,p] for p in side) def nz_kan_niets(t): """True als NZ in GEEN denominatie een makend contract heeft. Makend: 3SA(9), 4H/4S(10), 4C/4D of 5C/5D -> we eisen >=10 in minor als 'makend deelscore/manche dat telt'. Streng: NZ nergens >=10, en NT nergens >=9.""" NZ=[Player.north,Player.south] if mx(t,Denom.nt,NZ)>=9: return False if mx(t,Denom.hearts,NZ)>=10 or mx(t,Denom.spades,NZ)>=10: return False if mx(t,Denom.clubs,NZ)>=10 or mx(t,Denom.diamonds,NZ)>=10: return False return True def run_A(N=2000, seed=1): print("=== RUN A: passen objectief best, over volledige populatie ===") rng=random.Random(seed); pas_best=0 for i in range(N): t=calc_dd_table(Deal(to_pbn(gen(rng)))) if nz_kan_niets(t): pas_best+=1 if (i+1)%500==0: print(f" {i+1}/{N}") print(f"\nPassen objectief best: {pas_best}/{N} ({100*pas_best/N:.1f}%)") print(f"NZ had wel een makend contract: {N-pas_best}/{N} ({100*(N-pas_best)/N:.1f}%)\n") def run_B(N=40000, seed=7, want=12): print("=== RUN B: waterdichte haakspellen (OW-manche + NZ niets) ===") rng=random.Random(seed) OW=[Player.east,Player.west]; g=0 for i in range(N): d=gen(rng); t=calc_dd_table(Deal(to_pbn(d))) ow_manche = mx(t,Denom.hearts,OW)>=10 or mx(t,Denom.spades,OW)>=10 if ow_manche and nz_kan_niets(t): g+=1 print("\n"+"="*55) print(f"HAAKSPEL #{g} (deal {i})") print(to_pbn(d)) t.pprint() print("Par:", par(t, vul=0, dealer=Player.west)) if g>=want: break print(f"\n{g} haakspellen gevonden in {i+1} deals.") if __name__=="__main__": run_A(N=2000, seed=1) run_B(N=40000, seed=7, want=12)