monopoly6.py

Created by elnix91

Created on September 28, 2025

17.7 KB

debug version monopoly, feel free to finish it; the last version is available here


from kandinsky import fill_rect as rect,draw_string as txt,set_pixel as pix
from random import randint,choice
from ion import keydown as k
import time
SKY=(0,0,0)
TXT=(255,)*3
IMP=(0,0,255)
SEL=(255,255,0)
PC=[(200,0,200),(0,255,255),(255,0,255),(255,200,0),(255,0,0),(255,255,0),(0,150,0),(0,0,255)]
savefile="monopoly.sav"
CASES=[#["name",type,id/amount,group-if-property,price,rent,house1,house2,house3,house4,hotel]
["Start",1,200],
["Mediterranean Avenue",0,0,0,60,2,10,30,90,160,250],
["Community Chest",5],
["Baltic Avenue",0,1,0,60,4,20,60,180,320,450],
["Income Tax",1,-200],
["Reading Railroad",0,22,1,200,25],
["Oriental Avenue",0,2,1,100,6,30,90,270,400,550],
["Chance",4],
["Vermont Avenue",0,3,1,100,6,30,90,270,400,550],
["Connecticut Avenue",0,4,1,120,8,40,100,300,450,600],
["Jail",6],
["St. Charles Place",0,5,2,140,10,50,150,450,625,750],
["Electric Company",0,26,5,150],
["States Avenue",0,6,2,140,10,50,150,450,625,750],
["Virginia Avenue",0,7,2,160,12,60,180,500,700,900],
["Pennsylvania Railroad",0,23,1,200,25],
["St. James Place",0,8,3,180,14,70,200,550,750,950],
["Community Chest",5],
["Tennessee Avenue",0,9,3,180,14,70,200,550,750,950],
["New York Avenue",0,10,3,200,16,80,220,600,800,1000],
["Free Parking",8],
["Kentucky Avenue",0,11,4,220,18,90,250,700,875,1050],
["Chance",4],
["Indiana Avenue",0,12,4,220,18,90,250,700,875,1050],
["Illinois Avenue",0,13,4,240,20,100,300,750,925,1100],
["B&O Railroad",0,24,1,200,25],
["Atlantic Avenue",0,14,5,260,22,110,330,800,975,1150],
["Ventnor Avenue",0,15,5,260,22,110,330,800,975,1150],
["Water Works",0,27,5,150],
["Marvin Gardens",0,16,5,280,24,120,360,850,1025,1200],
["Go to Jail",7],
["Pacific Avenue",0,17,6,300,26,130,390,900,1100,1275],
["North Carolina Avenue",0,18,6,300,26,130,390,900,1100,1275],
["Community Chest",5],
["Pennsylvania Avenue",0,19,6,320,28,150,450,1000,1200,1400],
["Short Line",0,25,1,200,25],
["Chance",4],
["Park Place",0,20,7,350,35,175,500,1100,1300,1500],
["Luxury Tax",1,-100],
["Boardwalk",0,21,7,400,50,200,600,1400,1700,2000]]

CHANCES=[
[["Go to jail!"],5],
[["Free from jail"],0],
[["Advance to Boardwalk"],2,21],
#[["Advance to Go","(Collect 200)"],2,0],
#[["Advance to Illinois","Avenue"],2,19],
#[["Advance to St. Charles","Place"],2,5],
#[["Bank pays you a","dividend of 50"],1,50],
#[["Get out of jail","free"],0],
#[["Go back 3 spaces"],4],
#[["Go to Reading","Railroad"],2,22],
#[["Make general repairs","on all property"],3,[25,100]],
#[["Pay poor tax of","150"],1,-150],
#[["Take a trip to","Pennsylvania Railroad"],2,24],
#[["Your building and","loan matures,","Collect 150"],1,150],
#[["You have won a","crossword competition.","Collect 100"],1,100]
]

CHESTS=[
[["Go to jail!"],5],
[["Free from jail"],0],
[["Advance to Go","(Collect 200)"],2,0],
#[["Bank error in your","favor, Collect 200"],1,200],
#[["Doctor's fees,","Pay 100"],1,-100],
#[["From sale of stock","you get 50"],1,50],
#[["Get out of jail","free"],0],
#[["Grand Opera Night.","Collect 50","from each player"],1,50],
#[["Holiday fund matures.","Receive 100"],1,100],
#[["Income tax refund.","Collect 20"],1,20],
#[["It is your birthday.","Collect 10","from each player"],6,10],
#[["Life insurance matures.","Collect 100"],1,100],
#[["Pay hospital fees","of 100"],1,-100],
#[["Pay school fees","of 50"],1,-50],
#[["Receive consultancy","fee of 250"],1,250],
#[["You inherit 100"],1,100]
]

def shuffle(l):
  n=len(l)
  for i in range(len(l)-1,0,-1):
    j=randint(0,i)
    l[i],l[j]=l[j],l[i]
  return l
try:
  import os
  OS=True
except:OS=False

clean=lambda:rect(0,0,320,222,SKY)
txtc=lambda t,y,fc,bc:txt(t,160-len(t)*5,y,fc,bc)
s=lambda:k(0)|k(1)|k(2)|k(3)
v=lambda:k(4)|k(52)

def popup(x,y,w,h,xso,yso,c,title,opt,types,result=[],frame=True,padding=2,back=False,retname=False):
  if frame:
    rect(x,y,w,h,c[0])
    rect(x+3,y+3,w-6,h-6,SKY)
    y+=2
  else:rect(x,y,w,h,SKY)
  for i,j in enumerate(title):
    txtc(j,y+2+(18+padding)*i,c[1],SKY)

  ntv=False
  for i,o in enumerate(opt):
    txt(o,xso,yso+(18+padding)*i,c[2],SKY)
    if types[i][0]==0:
      switch(types[i][1],yso+(18+padding)*i,result[i])
      ntv=True
    elif types[i][0]==1:
      rect(types[i][1],yso+(18+padding)*i+17,10*types[i][2],1,IMP)
      ntv=True
    elif types[i][0]==2:
      txt("<   >",types[i][1]-20,yso+(18+padding)*i,IMP,SKY)
  if ntv:
    txt("VALIDATE",xso,yso+(18+padding)*(i+1),(0,255,0),SKY)
    types.append([3])
  r=0
  first=True
  lastAct=-1
  while v():1
  while 1:
    if s()|first:
      lastAct=r
      r=(r-k(1)+k(2))%len(types)
      if types[r][0]==2:
        result[r]=(result[r]-k(0)+k(3))%(types[r][3]-types[r][2]+1)
        txt(str(result[r]+types[r][2]),types[r][1],yso+(18+padding)*r,IMP,SKY)
      rect(xso-20,yso+(18+padding)*lastAct,10,18,SKY)
      txt("",xso-20,yso+(18+padding)*r,SEL,SKY)
      if first:first=False
      while s():1

    elif v():
      if not ntv:
        if retname:return opt[r]
        return r
      if types[r][0]==0:
        result[r]=not result[r]
        switch(types[r][1],yso+(18+padding)*r,result[r])
        while v():1
      elif types[r][0]==1:
        result[r]=kinput(types[r][1],yso+(18+padding)*r,types[r][2],types[r][3],types[r][4])
      
      elif types[r][0]==3:
        if retname:
          return opt[r]
        return tuple(result)

    elif k(17)&back:return -1

def switch(x,y,on):
  rect(x,y,18,18,(200,)*3)
  rect(x+1,y+1,16,16,SKY)
  if on:c=(0,255,0)
  else:c=(100,)*3
  rect(x+3,y+3,12,12,c)

def kinput(x,y,maxLenght=4,minLenght=4,onlynumbers=True,ct=TXT):
  letters={29: 'l', 30: 'm', 31: 'n', 32: 'o', 33: 'p', 34: 'q', 36: 'r', 37: 's', 38: 't', 39: 'u', 40: 'v', 42: 'w', 43: 'x', 44: 'y', 45: 'z', 18: 'a', 19: 'b', 20: 'c', 21: 'd', 22: 'e', 23: 'f', 24: 'g', 25: 'h', 26: 'i', 27: 'j', 28: 'k'}
  numbers={30: '7', 31: '8', 32: '9', 42: '1', 43: '2', 44: '3', 36: '4', 37: '5', 38: '6', 48: '0'}
  Input=""
  rect(x,y,10*maxLenght,18,SKY)
  rect(x,y+17,10*maxLenght,1,IMP)
  rect(x,y+2,1,14,TXT)
  while v():1
  while 1:
    for i in range(17,49):
      if k(i):
        if onlynumbers:
          if(i in numbers.keys())&(len(Input)<maxLenght):
            Input+=numbers[i]
        else:
          if(i in letters.keys())&(len(Input)<maxLenght):
            Input+=letters[i]
        if (i==17)&(len(Input)>0):
          Input=Input[:-1]
          rect(x+len(Input)*10,y,12,18,SKY)
        rect(x,y+17,10*maxLenght,1,IMP)
        rect(x+len(Input)*10+1,y+2,1,14,TXT)
        txt(Input,x,y,ct,SKY)
        while k(i):1
    if v():
      if len(Input)>=minLenght:
        rect(x+len(Input)*10+1,y+2,1,14,SKY)
        return Input
      else:
        txt(Input,x,y,(255,0,0),SKY)
        time.sleep(0.5)
        txt(Input,x,y,ct,SKY)


def draw_case(x,y,id,rot=0):
  t=CASES[id][1]
  rect(x,y,20,20,TXT)
  rect(x+1,y+1,18,18,SKY)
  if t==0:
    if 21<CASES[id][2]<26: #Train stations
      rect(x+2,y+2,16,16,(50,)*3)
      rect(x+4,y+4,12,12,SKY)
      rect(x+6,y+6,8,8,(50,)*3)
    elif CASES[id][2]>25: # Services
      if CASES[id][2]==0:c=(0,0,200)
      else:c=SEL
      txt("S",x+5,y+1,c,SKY)

    else: # Propreties
      xs1,ys1,w1,h1,xs2,ys2,w2,h2=x+1,y+1,18,5,x+1,y+7,18,12
      if rot==1:xs1,w1,h1,ys2,w2,h2=x+14,5,18,y+1,12,18
      elif rot==2:ys1,ys2=y+14,y+1
      elif rot==3:w1,h1,xs2,ys2,w2,h2=5,18,x+7,y+1,12,18
      rect(xs1,ys1,w1,h1,PC[CASES[id][3]])
      for i in range(N):
        if id in A[i]["terrains"]:
          rect(xs2,ys2,w2,h2,PC[i])

      if 0<A[-2][CASES[id][2]]<5:
        xi,yi=0,0
        if w1>h1:xi=4
        else:yi=4
        for i in range(A[-2][CASES[id][2]]):
          rect(xs1+1+xi*i,ys1+1+yi*i,3,3,SKY)
      elif A[-2][CASES[id][2]]==5:
        rect(xs1+1,ys1+1,3,3,TXT)
  elif t==1: # Start/Taxes
    if CASES[id][2]>0:c=(0,200,0)
    else:c=(200,0,0)
    rect(x+2,y+2,16,16,c)
  elif 3<t<6: # Chance/Community Chest
    if t==4:c=(0,0,255)
    else:c=(255,0,0)
    txt("C",x+5,y+1,c,SKY)
  elif t==6: # Jail
    rect(x+1,y+1,18,18,(255,100,0))
    rect(x+1,y+7,12,12,(70,)*3)
  elif t==7:rect(x+3,y+3,14,14,(255,100,0)) # Go to jail
  elif t==8: # Free park
    rect(x+2,y+2,16,16,(0,255,0))
    rect(x+4,y+4,12,12,(0,180,0))
# Draw players
  np=[]
  for i in range(N):
    if A[i]["case"]==id:
      np.append(i)
  if len(np)>0:
    rect(x+5,y+7,10,6,SKY)
    for _ in range(2):
      for __ in range(4):
        if _*4+__ in np:rect(x+6+__*2,y+8+_*2,2,2,PC[_*4+__])


  

def getHousePrice(id):
  if id<10:return 50
  elif id<20:return 100
  elif id<30:return 150
  else:return 200

def draw_terrain():
  clean()
  id=0
  for i in range(10):
    draw_case(1+i*20,1,id,0)
    id+=1
  for i in range(10):
    draw_case(201,1+i*20,id,1)
    id+=1
  for i in range(10):
    draw_case(201-i*20,201,id,2)
    id+=1
  for i in range(10):
    draw_case(1,201-i*20,id,3)
    id+=1
  txt("Total in",70,50,IMP,SKY)
  txt("Free park:",60,70,IMP,SKY)
  txt(str(A[-1]),110-len(str(A[-1]))*5,90,IMP,SKY)

def calc_rent(id,All=False,owner=None):
  p=[200,25,50,100,200]
  if CASES[id][2]<26:
    if All:
      if CASES[id][2]<22:return CASES[id][4:]
        # # p=[CASES[id][4]] #Case price
        # for i in range(7):
        #   p.append(CASES[id][4+i]) # CasePrice/Greenfield/houses/hotel
      return p
    else:
      if CASES[id][2]<22:
        return CASES[id][4+A[-2][CASES[id][2]]]
      else:
        nbts=0
        for i in range(22,26):
          if i in A[owner]["terrains"]:nbts+=1
        return p[nbts+1]
  else:
    if All:return
    nbs=0
    if 28 in A[owner]["terrains"]:nbs+=1
    if 27 in A[owner]["terrains"]:nbs+=1
    if nbs==1:return 400*DR
    return 1000*DR


def draw_rent(x,y,id):
  r=calc_rent(id,True)
  if CASES[id][2]<26:
    for i,j in enumerate(r):
      if i==0:txt("Base Price:",x,y,TXT,SKY)
      if CASES[id][2]<22:
        if i==1:txt("Terrain:",x,y+20,TXT,SKY)
        elif i==6:txt("Hotel:",x,y+120,TXT,SKY)
        elif i>0:txt(str(i-1)+" houses",x,y+20*i,TXT,SKY)
        txt(str(j),x+120,y+20*i,PC[4],SKY)
      else:
        if i>0:txt(str(i)+" stations:",x,y+20*i,TXT,SKY)
        txt(str(j),x+120,y+20*i,PC[4],SKY)
  else:
    txt("1 serv: 400*Dices",x,y,TXT,SKY)
    txt("2 serv: 1000*Dices",x,y+20,TXT,SKY)

def selectP(l):
  clean()
  txtc("Select a propriety:",10,IMP,SKY)
  act=0
  first=True
  while v():1
  while not v():
    if s()|first:
      act=(act-k(0)+k(3))%len(l)
      clean()
      rect(0,40,320,18,SKY)
      txtc("< "+CASES[l[act]][0]+" >",40,TXT,SKY)
      draw_case(20,100,l[act])
      draw_rent(110,75,l[act])
      if first:first=False
      while s():1
    elif k(17):
      return -1
  return l[act]
        
def drawC(P,t):
  clean()
  rp=0
  if t:
    id=-5
    c=CHANCES[A[P]["case"]][1]
    txtc("CHANCE",10,IMP,SKY)
  else:
    id=-4
    c=CHESTS[A[P]["case"]][1]
    txtc("COMMUNITY CHEST",10,IMP,SKY)
  for i,j in enumerate(CHANCES[A[id][0]][0]):
    txtc(j,35+i*20,TXT,SKY)
  
  if c==0: # Free from jail
    A[P]["free"]=True
  elif c==1: # Pay/gain amount
    if t:amount=CHANCES[A[-5][0]][2]
    else:amount=CHESTS[A[-4][0]][2]
    if amount>0:A[P]["money"]+=amount
    else:pay(P,-1,amount,True)
  elif c==2: # Move to
    pass
  elif c==3: #Pay houses
    nbhouses=0
    nbhotels=0
    for i in A[P]["terrains"]:
      n=A[-2][CASES[i][2]]
      if 0<n<5:nbhouses+=n
      else:nbhotels=+n
    amount=nbhouses*CHANCES[A[-5][0]][2]+nbhotels*CHANCES[A[-5][0]][3]
    rp=pay(P,-1,amount)
  elif c==4: # move backwards
    pass
  elif c==5: # Go jail
    pass
    
  
  if c:A[id].append(A[id][0])
  del [id][0]
  return rp



#-1:cancel,0:fine,1:one lost, 2:game ends
def pay(p,r,amount,middle=False,canCancel=False):
  while A[p]["money"]<amount:
    opt=[]
    houses={}
    for i in A[p]["terrains"]:
        if CASES[i][2]<22:houses[i]=A[-2][CASES[i][2]]
        else: houses[i]=0
    if sum(houses.values())>0:
      opt.append("Sell houses")
    if 0 in houses.values():opt.append("Mortgage")
    if len(opt)==0:
      if lose(p):return 1
      else:return 2
    code=popup(10,10,300,202,40,90,[PC[4],PC[4],IMP],["Warning! Not enough money!","Your money: "+str(A[p]["money"]),"To pay: "+str(amount)],opt,[[3] for i in range(len(opt))],back=canCancel,retname=True)
    if code=="Sell houses":
      htcbs=[i for i,j in houses.items() if j>0]
      s=selectP(htcbs)
      if s==-1:continue
      else:
        A[-2][CASES[s][2]]-=1
        A[p]["money"]+=getHousePrice(s)//2
        continue
        
    elif code=="Mortgage":
      htcbs=[i for i,j in houses.items() if j==0]
      s=selectP(htcbs)
      if s==-1:continue
      else:
        A[p]["terrains"].remove(s)
        A[p]["hyp"].append(s)
        A[p]["money"]+=CASES[s][4]//2

    else:return -1 # Cancel

  A[p]["money"]-=amount
  if middle:A[-1]+=amount
  if r!=-1:A[r]["money"]+=amount
  return 0

def lose(p):
  N-=1
  for i in A[p]["hyp"]:
    A[-3].append(i)
  rect(20,20,280,182,PC[p])
  rect(23,23,274,176,SKY)
  if N>1:
    txtc(A[p]["name"]+" lose!",70,PC[p],SKY)
    txtc("BUT THE GAME CONTINUE!",110,TXT,SKY)
  del A[p]
  if N==1:
    txtc("Congrat to "+A[0]["name"],70,PC[p],SKY)
    txtc("For winning this game!",110,TXT,SKY)
    while v():1
    while not v():1
    return False
  else:
    while v():1
    while not v():1
    return True

def drawDice(x,y,n):
  rect(x,y,20,20,TXT)
  rect(x+1,y+1,18,18,SKY)
  txt(str(n),x+5,y+1,PC[5],SKY)

while 1:
  clean()
  c=0
  m=True
  if OS:
    if savefile in os.listdir():
      c=popup(0,0,320,222,135,150,[PC[0],PC[3],IMP],["Savefile found!","Do you want to","continue the game?"],["YES","NO"],[[3],[3]])
      if c==0:
        with open(savefile,"r")as f:
          A=eval(f.read())
        if"code"in A[0].keys():Password=True
        else:Password=False
        N=len(A[-6])
        Save=True
        m=False

  if m==True:
    opt=["Number of players:","Password protection:"]
    types=[[2,260,2,8],[0,260]]
    if OS:
      opt.append("Enable saving:")
      types.append([0,260])
    params=popup(0,0,320,222,30,70,[PC[1],PC[3],IMP],["MONOPOLY - Made by Elnix"],opt,types,[0,False,False],padding=22)
    N,Password,Save=params[0]+2,params[1],params[2]
    del params
    A=[{"name":"","money":1500,"terrains":[],"hyp":[],"case":0,"free":False} for i in range(N)]
    for i in range(N):
      clean()
      txt("Player "+str(i+1),30,20,PC[i],SKY)
      txt(", enter your name:",110,20,TXT,SKY)
      A[i]["name"]=kinput(110,70,10,1,False,PC[i])
      if Password:
        txt("Please set a up a pin code:",25,110,TXT,SKY)
        while 1:
          code=kinput(140,140)
          if len(code)<4:txt("Too short!",110,170,(255,0,0),SKY)
          else:
            A[i]["code"]=code
            break
    A.append([33,11])
    A.append(shuffle([i for i in range(18)]))
    A.append(shuffle([i for i in range(18)]))
    A.append([i for i in range(22)])
    A.append([0 for i in range(22)])
    A.append(0)
    
  T=randint(0,N-1)
  DR=randint(1,6)+randint(1,6)
  clean()

  # Tests
  A[0]["terrains"].append(1)
  A[0]["terrains"].append(3)
  A[0]["terrains"].append(5)
  A[0]["terrains"].append(6)
  A[0]["terrains"].append(12)
  for i in range(22):
    A[-2][i]=randint(1,5)


  draw_terrain()
#  print(pay(0,1,9899999,canCancel=True))
  ###
  while 1:
    if Password:
      clean()
      txtc(A[T]["name"]+", enter your password:",50,TXT,SKY)
      while kinput(140,110)!=A[T]["code"]:
        txtc("Wrong password, retry in 3s...",130,PC[4],SKY)
        time.sleep(3)

    # Dices animation
    draw_terrain()
    wasRewarded=0
    d1,d2=randint(1,6),randint(1,6)
    t=time.monotonic()
    while time.monotonic()-t<0.2:
      drawDice(80,150,randint(1,6))
      drawDice(120,150,randint(1,6))
    drawDice(80,150,d1)
    drawDice(120,150,d2)
    txt(str(d1),85,150,PC[5],SKY)
    txt(str(d2),120,150,PC[5],SKY)
    DR=d1+d2
    while v():1
    while not v():1


    A[T]["case"]=(A[T]["case"]+DR)%40
    if A[T]["case"]-DR<0:wasRewarded=200
    elif A[T]["case"]==0:
      wasRewarded=400
    A[T]["money"]+=wasRewarded
    
    clean()
    rp=0
    a=CASES[A[T]["case"]][1]
    txt("Landed on "+CASES[A[T]["case"]][0],5,5,TXT,SKY)
    if wasRewarded:txtc("You were rewarded by "+str(wasRewarded),202,(0,255,0),SKY)
    draw_case(10,55,A[T]["case"])

    
    if a==0: # Pay rent/Buy propriety
      draw_rent(110,60,A[T]["case"])
      txt("Money:",0,90,TXT,SKY)
      txt(str(A[T]["money"]),0,110,IMP,SKY)
      if A[T]["case"] in A[-3]:
        c=popup(-1,30,0,0,20,170,[0,(0,255,0),IMP],["Want you buy this propriety?"],["YES","NO"],[[3],[3]],frame=False)
        if c==0:
          rp=pay(T,-1,CASES[A[T]["case"]][4],canCancel=True)
          if rp==0:
            A[-3].remove(A[T]["case"])
            A[T]["terrains"].append(A[T]["case"])
      elif A[T]["case"] not in A[T]["terrains"]:
        # find who own the case
        for i in range(N):
          if A[T]["case"] in A[i]["terrains"]:
            amount=calc_rent(A[T]["case"],owner=i)
            txtc("You have to pay "+A[i]["name"],30,(255,150,0),SKY)
            txtc(str(amount),50,(255,150,0),SKY)
            txtc("[OK]",50,IMP,SKY)
            while v():1
            while not v():1
            rp=pay(T,i,amount)
            break
        if rp!=0:continue


    elif a==1: # Gain/pay taxe
      amount=CASES[A[T]["case"]][2]
      if amount>0:
        txtc("You have been rewarded by",30,IMP,SKY)
        txtc(str(amount),50,(0,255,0),SKY)
        A[T]["money"]+=amount
      else:
        txtc("You have to pay",30,IMP,SKY)
        txtc(str(amount),50,(255,0,0),SKY)
        rp=pay(T,-1,amount,True)
        if rp!=0:continue

    elif 4: # Chance
#      txtc("You draw a chance",30,IMP,SKY)
      drawC(T,1)
    elif 5: # Chest
#      txtc("You draw a community chest",30,IMP,SKY)
      drawC(T,0)
    
    elif a==6: # Jail
      pass
    elif a==7: # Go to jail
      pass
    elif a==8: # Free park
      txtc("Congrats, you won the middle!")
      A[T]["money"]+=A[-1]
      A[-1]=0

    # Options that you can do here
    opt=[]
    

    if Save:
      with open(savefile,"w")as f:
        f.write(str(A))
    T=(T+1)%N
  if OS:os.remove(savefile)

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.