找回密码
 立即注册
搜索
热搜: 活动 交友
查看: 2305|回复: 5

Threes!游戏及其AI的Python实现

[复制链接]
39 金钱 回复本帖可获得 3 金钱奖励! 每人限 2 次(中奖概率 80%)

28

主题

68

回帖

2009

积分

版主

积分
2009
发表于 7-5-2025 14:39:44 | 显示全部楼层 |阅读模式

〇、 游戏简介


Threes!
是一款由Sirvo LLC开发并发行的益智类游戏,于2014年2月10日发布,游戏平台为iOS。



Threes!为2048的灵感来源。


Threes!曾获得2014年度App Store中国区iPhone年度最佳游戏的荣誉。






一、 游戏规则

它的主要规则如下:

1. 盘面大小为4*4



2. 盘面由1,2,3,6,12,24,48,...,3072,6144,12288和空格组成,合成12288后游戏通关 (人力打到3072就很强了)


3. 相邻的1和2可以合成3,相邻的相同的3*2^k可以合成3*2^(k+1)
  例如: 3和3合成6,6和6合成12

4. 每次可以将盘面向上下左右中的一个方向移动,其间每一行每一列都有可能可以移动或无法移动





5. (每次移动)(合并)(移动方向上)(无法平移)且(可以合并)的(第一组相邻数字块)并(平移剩下数字块与空格)并(添加一块空格)
  例如: 1212→0123 1210→0121 1332→0162 3033→0306


6. 下一块为1,2,3或大数字块,其中大数字块为3选1,中间那个的取值范围为[12,maxnum/16]
  例如: maxnum=192,大数字块必为[[6,12,24]三选一];
      maxnum=1536,大数字块为[[6,12,24]三选一,[12,24,48]三选一,[24,48,96]三选一,[48,96,192]三选一]四选一


7. 盘面上如果1比2多4块,则下一块必不为1,反之亦然。


8. 下一块必出现在下一次发生移动的随机一行内


9. 无法移动则游戏结束


10. 开局时有一个大数字块和7个1,2,3数字块和8个空格 (本人代码实现时将大数字块设为192)






二、游戏技巧


欢迎大家讨论,并贴出自己的最高分!






三、代码实现


因为希望有同学也可以做出这个游戏的AI,所以本贴直接贴出可运行的无AI版代码。

<blockquote>import random,pygame,sys,time



四、实现AI





上面四张图是同一局游戏的截图,已经实现了AI合成6144+3072 (可以上手一下看看有多难)


附上[url=【Threes! AI 合成6144+3072】https://www.bilibili.com/video/BV1zd3bzoEvL]视频链接[/url] [url=【Threes! AI 合成6144+3072】https://www.bilibili.com/video/BV1zd3bzoEvL]【Threes! AI 合成6144+3072】https://www.bilibili.com/video/BV1zd3bzoEvL[/url] (在挑战高分的过程中可以借鉴打法)


回帖>=10 或 回帖不同人数>=5 或 有同学从192手打出1536: 给出基础AI实现思路


回帖>=25 或 回帖不同人数>=10: 给出基础AI代码截图


有同学做出从192合成1536的AI 或 从192手打出3072: 给出进阶AI实现思路 (手打记得录屏!)

ps: 该AI开发难度略低于五子棋AI,简单易上手

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

评分

参与人数 4威望 +10 金钱 +95 收起 理由
vbro123 + 5 + 50 很给力!
C0mp1ler + 20 很给力!
littleblackLB + 20 赞一个!
脆脆大奶酪 + 5 + 5 赞一个!

查看全部评分

24

主题

58

回帖

1525

积分

版主

积分
1525
发表于 7-5-2025 15:24:51 | 显示全部楼层

回帖奖励 +3 金钱

1+1≠2真是太出生了

24

主题

58

回帖

1525

积分

版主

积分
1525
发表于 7-5-2025 15:26:44 | 显示全部楼层

回帖奖励 +3 金钱


哦不对,2+2也≠4

24

主题

58

回帖

1525

积分

版主

积分
1525
发表于 7-5-2025 18:26:06 | 显示全部楼层
挑战最低分

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

28

主题

68

回帖

2009

积分

版主

积分
2009
 楼主| 发表于 7-15-2025 18:05:24 | 显示全部楼层

28

主题

68

回帖

2009

积分

版主

积分
2009
 楼主| 发表于 7-15-2025 18:05:57 | 显示全部楼层
import random,pygame,sys,time

def copy(board):return [row[:] for row in board] #copies a board

def maxnum(board):return max(max(row) for row in board) #finds the max number in a board

def rr(board):return [list(row) for row in zip(*board[::-1])] #rotates a board clockwise

def flp(board):return [[board[j][i] for j in range(len(board))] for i in range(len(board[0]))] #flips a board diagonally

def score(board):return sum(3**(x-2) for row in board for x in row if x>2) #scores a board
    
def up(board): #moves a board upwards
    nBoard=copy(board)
    moved=[0,0,0,0] #moved columns
    for y in range(4): #iterates all 4 columns
        for x in range(3): #iterates from 1st row to the 3rd row
            if nBoard[x][y]==0 and nBoard[x+1][y]!=0: #an empty block with a non-empty neighbor block below it
                moved[y]=1
                nBoard[x][y]=nBoard[x+1][y]
            elif nBoard[x][y]==1 and nBoard[x+1][y]==2 or nBoard[x][y]==2 and nBoard[x+1][y]==1: #merge 1 and 2
                moved[y]=1
                nBoard[x][y]=3
            elif nBoard[x][y]==nBoard[x+1][y] and nBoard[x][y]>=3: #merge same numbers
                moved[y]=1
                nBoard[x][y]+=1
            if moved[y]==1: #move numbers below
                for i in range(x+1,3):
                    nBoard[i][y]=nBoard[i+1][y]
                nBoard[3][y]=0
                break
    return nBoard,moved

def down(board):
    nBoard,moved=up(rr(rr(board)))
    return rr(rr(nBoard)),moved[::-1]

def left(board):
    nBoard,moved=up(rr(board))
    return rr(rr(rr(nBoard))),moved[::-1]

def right(board):
    nBoard,moved=up(rr(rr(rr(board))))
    return rr(nBoard),moved

def next(board,moved):
    if sum(moved)==0:
        return nNum
    ones=sum(row.count(1) for row in board)
    twos=sum(row.count(2) for row in board)
    if twos-ones>=4: #no twos if already many twos
        weights=[20,0,10,1]
    elif ones-twos>=4: #no ones if already many ones
        weights=[0,20,10,1]
    else:
        weights=[10+2*(twos-ones),10+2*(ones-twos),10,1] #weight also floats depending on current ones and twos
    n=random.choices([1,2,3,4],weights=weights,k=1)[0]
    if n!=4:
        return n
    m=maxnum(Board)
    mid=random.randint(5,m-4)
    return mid

def insert(board,moved,nNum,dir):
    if sum(moved)==0: #unable to insert
        return board
    if nNum>=5:
        nNum=random.choice([nNum-1,nNum,nNum+1])
    nBoard=copy(board)
    i=random.choices([0,1,2,3],weights=moved,k=1)[0] #choses a moved row or column to insert
    if dir==0:
        nBoard[3][i]=nNum
    if dir==1:
        nBoard[0][i]=nNum
    if dir==2:
        nBoard[i][3]=nNum
    if dir==3:
        nBoard[i][0]=nNum
    return nBoard

def draw(scr):
    scr.fill((235,235,235))
    pygame.draw.line(scr,(23,23,23),(10,10),(410,10))
    pygame.draw.line(scr,(23,23,23),(10,160),(410,160))
    pygame.draw.line(scr,(23,23,23),(10,310),(410,310))
    pygame.draw.line(scr,(23,23,23),(10,460),(410,460))
    pygame.draw.line(scr,(23,23,23),(10,610),(410,610))
    pygame.draw.line(scr,(23,23,23),(10,10),(10,610))
    pygame.draw.line(scr,(23,23,23),(110,10),(110,610))
    pygame.draw.line(scr,(23,23,23),(210,10),(210,610))
    pygame.draw.line(scr,(23,23,23),(310,10),(310,610))
    pygame.draw.line(scr,(23,23,23),(410,10),(410,610)) #draws the grids
    for x in range(4):
        for y in range(4):
            num=Board[x][y] #draws tiles
            if num==0:
                color2=(220,220,220)
                pygame.draw.rect(scr,color2,(100*y+20,150*x+20,80,130))
                continue
            if num>2:
                color1=(23,23,23)
                color2=(242,242,242)
                pygame.draw.rect(scr,color2,(100*y+20,150*x+20,80,130))
                num=3*2**(num-3)
            if num==1:
                color1=(255,235,235)
                color2=(23,180,255)
                pygame.draw.rect(scr,color2,(100*y+20,150*x+20,80,130))
            if num==2:
                color1=(235,255,255)
                color2=(255,90,135)
                pygame.draw.rect(scr,color2,(100*y+20,150*x+20,80,130))
            d=len(str(num))
            if d==1:
                s=50
            if d==2 or d==3:
                s=40
            if d==4:
                s=32
            if d>=5:
                s=26
            font=pygame.font.SysFont('Monaco',s)
            img=font.render(str(num),True,color1,color2)
            img_rect=img.get_rect()
            img_rect.center=(60+100*y,85+150*x)
            scr.blit(img,img_rect)
    font=pygame.font.SysFont('Monaco',45)
    img=font.render('Next Num',True,(23,23,23),(235,235,235))
    img_rect=img.get_rect()
    img_rect.center=(580,80)
    scr.blit(img,img_rect)
    if nNum<=3:
        font=pygame.font.SysFont('Monaco',50)
        color=(23,23,23)
        if nNum==1:
            color=(40,40,192)
        if nNum==2:
            color=(192,40,40)
        img=font.render(str(nNum),True,color,(235,235,235))
        img_rect=img.get_rect()
        img_rect.center=(580,170)
        scr.blit(img,img_rect)
    if nNum>=5: #here 5 stands for 6,12,24, 8 stands for 48,96,192, etc.
        color=(23,23,23)
        n=3*2**(nNum-4)
        font=pygame.font.SysFont('Monaco',50-8*(n>100))
        img=font.render(str(n),True,color,(235,235,235))
        img_rect=img.get_rect()
        img_rect.center=(490,170)
        scr.blit(img,img_rect)
        n=3*2**(nNum-3)
        font=pygame.font.SysFont('Monaco',50-8*(n>100))
        img=font.render(str(n),True,color,(235,235,235))
        img_rect=img.get_rect()
        img_rect.center=(580,170)
        scr.blit(img,img_rect)
        n=3*2**(nNum-2)
        font=pygame.font.SysFont('Monaco',50-8*(n>100))
        img=font.render(str(n),True,color,(235,235,235))
        img_rect=img.get_rect()
        img_rect.center=(670,170)
        scr.blit(img,img_rect)
    font=pygame.font.SysFont('Monaco',45)
    img=font.render('Score',True,(23,23,23),(235,235,235))
    img_rect=img.get_rect()
    img_rect.center=(580,250)
    scr.blit(img,img_rect)
    img=font.render(str(score(Board)),True,(23,23,23),(235,235,235)) #draws the score
    img_rect=img.get_rect()
    img_rect.center=(580,340)
    scr.blit(img,img_rect)
    pygame.display.update()

Board=[[0,0,1,1],
       [2,3,0,2],
       [0,0,3,0],
       [1,1,0,9]] #initial board
moved=[1,1,1,1]
nNum=next(Board,moved)
pygame.init()
screen=pygame.display.set_mode((800,620))
pygame.display.set_caption('Threes!'*random.choice([1,3]))
draw(screen)

while True:
    for event in pygame.event.get():
        if event.type==pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type==pygame.KEYDOWN:
            if event.key==pygame.K_UP:
                Board,moved=up(Board)
                Board=insert(Board,moved,nNum,0)
            elif event.key==pygame.K_DOWN:
                Board,moved=down(Board)
                Board=insert(Board,moved,nNum,1)
            elif event.key==pygame.K_LEFT:
                Board,moved=left(Board)
                Board=insert(Board,moved,nNum,2)
            elif event.key==pygame.K_RIGHT:
                Board,moved=right(Board)
                Board=insert(Board,moved,nNum,3)
            elif event.key==pygame.K_BACKSPACE: #resets game
                Board=[[0,0,1,1],
                       [2,3,0,2],
                       [0,0,3,0],
                       [1,1,0,9]]
                moved=[1,1,1,1]
                nNum=next(Board,moved)
                draw(screen)
            else:
                continue
            nNum=next(Board,moved)
            draw(screen)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|RealDevClub ( 沪ICP备2024093864号-1 )

GMT+8, 3-22-2026 05:22 , Processed in 0.099413 second(s), 29 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表