单人扑克游戏:地城恶棍的Python实现(纯代码)

单人扑克游戏:地城恶棍的Python实现(纯代码)

实现细节链接:传送门

import random as ra
import numpy as np
from tqdm import tqdm
import os
import re
import time

class PlayingCard(object):
    def __init__(self) -> None:
        """
        构建牌库[x,y] 
        获取公式
        x = 54 // 4
        y = 54 % 4
        
        x \in [0,13],其中0~9代表数值1~10, 10/11/12代表J/Q/K, 13代表特殊牌(即王牌)
        y \in [0,3],其中0代表方块,1代表梅花,2代表桃心,3代表黑桃
        [13,0] 小王; [13,1] 大王
        """
        self.library = np.linspace(1,54,54).astype(np.int8)

        self.prefixs = ['\033[31m♦\033[0m','♣','\033[31m♥\033[0m','♠']
        self.suffixs = [str(i) for i in range(1,11)] + ['J','Q','K']
        self.specials = ["♚","\033[31m♚\033[0m"]
    
    def card2index(self,index) -> tuple:
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        index -= 1
        x,y = index//4,index % 4
        return (x,y)
    
    def card2str(self,index) -> str:
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        x,y = self.card2index(index)
        return self.prefixs[y] + self.suffixs[x]
    
    def shuffle(self,seed = None) -> np.array:
        #洗牌,掩膜也会被同步洗牌
        
        if seed is None:
            seed = ra.randint(0,2^16)
        ra.seed(seed)
        ra.shuffle(self.library)
        return self.library

    def _inLibrary(self,index) -> bool:
        #判断是否在卡里?
        return np.where(self.library == index)[0].size > 0
    
    def drop(self,indexList) -> np.array:
        #丢弃某张牌
        for index in indexList if type(indexList) == list else [indexList]:
            assert self._inLibrary(index), "[WARN] Card %d is not in library!" % index
            self.library = np.delete(self.library,
                            np.where(self.library == index))
        return self.library
    
    def reload(self) -> np.array:
        #获得一副新牌
        self.library = np.linspace(1,54,54).astype(np.int8)
        return self.library
    
    def getPrefix(self,index) -> int:
        """
        得到扑克牌花色
        其中0代表方块,1代表梅花,2代表桃心,3代表黑桃
        """
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        return (index - 1) % 4
    
    def getNumber(self,index) -> int:
        #得到扑克牌数字
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        return (index - 1) //4
    
    def pop(self) -> int:
        #取出牌库顶牌
        if len(self) == 0:
            return 0
        pop = self.library[0]
        self.library = self.library[1:]
        return pop
    
    def insert(self,index:int) -> np.array:
        #在牌库底插入牌
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        assert not self._inLibrary(index), "[WARN] Card %d is already in library!" % index
        self.library = np.concatenate([self.library,np.array([index])])
        return self.library
    
    def __str__(self):
        string = "Card Summary: %d\n" % len(self)
        for x in range(13): #用于显示每一行(本来可以继续压缩的,但是怕可读性不行)
            string += "\t".join([self.prefixs[y] + self.suffixs[x] for y in range(4) 
                              if self._inLibrary(4 * x + y + 1)]) + "\n"
        string += "\t".join([self.specials[i] for i in [0,1] if self._inLibrary(53 + i)])
        return string
    
    def __len__(self):
        return len(self.library)

class Config(object):
    def __init__(self,maxHealth) -> None:
        self.maxHealth = maxHealth
        self.health = maxHealth
        self.weapon = 0
        self.finalKill = 0
        
    def configReload(self):
        self.health = self.maxHealth
        self.weapon = 0
        self.finalKill = 0
        print("[INFO] Player Health: %d/%d" % (self.health,self.maxHealth))

class Game(Config):
    def __init__(self,maxHealth:int = 20,savePath = './checkpoint',seed = None) -> None:
        super().__init__(maxHealth)
        self.card = PlayingCard()
        self.room = np.zeros(4,dtype = np.int8)
        self._step = 0
        self.actions = None
        self._isSwitch = False #一个回合中只能刷新一次房间
        self.playTime = time.strftime("%Y-%m-%d=%H-%M", time.localtime()) 
        self.fileName = os.path.join(savePath,self.playTime)
        if not os.path.exists(savePath):
            os.mkdir(savePath)
        with open(self.fileName,'w') as file: pass #创建文件夹
        
        if seed is None:
            seed = ra.randint(0,2^16)
        self.reload(seed)
        
        
    def reload(self,seed = None) -> None:
        """
        该游戏不需要红色J,K,Q,A以及小王大王
        可以设定随机种子来保证公平对战
        """
        if seed is None:
            seed = ra.randint(0,2^16)
        self.configReload()
        self.card.reload()
        self.card.drop([(2 * i + 1) for i in [0,1,20,21,22,23,24,25]] + [53,54])
        self.card.shuffle(seed)
        print("[INFO] The game initialized successfully!")
    
    def _isEnd(self) -> int:
        """
        游戏结束机制:
        1. 卡池只剩一张,返回1
        2. 生命值少于0,返回-1
        """
        if len(self.card) == 0: return 1
        if self.health <= 0: return -1
        return 0
    
    def _updateRoom(self) -> None:
        """
        更新地牢房间:
        在牌库中抽牌,直至四张牌朝上形成一个房间
        """
        for index in np.where(self.room == 0)[0]:
            self.room[index] = self.card.pop()

    def __len__(self) -> int:
        return self._step
            
    def __str__(self):
        string = "Step:%d\t Left: %d" % (len(self),len(self.card)) + "\n" + "=" * 40
        string += "\nRoom:\t" +"\t".join([self.card.card2str(index) for index in self.room])
        string += "\n\n\nWeapon:\t%s" % (self.card.card2str(self.weapon) if self.weapon else "None")
        string += " (" + self.card.card2str(self.finalKill) + ")" if self.finalKill else ""
        string += "\nHealth:\t%d/%d" % (self.health,self.maxHealth)
        return string
    
    def _decode(self,action):
        if not action: return -1
        action = [int(index) for index in re.findall(r"\d",action)]
        
        for index in action:
            if index == 0:
                if not self._isSwitch:
                    self.actions = 0
                    return 1
                else:
                    print("[WARN] You can't switch room twice in one round!")
                    input("Press \"Enter\" key to continute")
                    return -1
    
            if index not in [1,2,3,4]:
                print("[WARN] Error action %d" % index)
                input("Press \"Enter\" key to continute")
                return -1
        if len(action) != 3:
            print("[WARN] The correct action length must be 3! Found %d" % len(action))
            input("Press \"Enter\" key to continute")
            return -1
        self.actions = action
        return 1
    
    def _switchRoom(self) -> None:
        self._isSwitch = True
        for index in self.room:
            self.card.insert(index)
            self.room = np.zeros(4,dtype = np.int8)
    
    def _action(self,action):
        """其中0代表方块,1代表梅花,2代表桃心,3代表黑桃"""
        index = self.room[action - 1]
        prefix = self.card.getPrefix(index)
        suffix = self.card.getNumber(index) + 1

        if suffix == 1: suffix = 14  #A代表14
            
        if prefix % 2 == 1: #方块和黑桃代表怪物
            print("[INFO] You meet the monster %s!" % self.card.card2str(index))
            if self.weapon != 0:
                damaged = suffix - self.card.getNumber(self.weapon 
                                         if self.finalKill == 0 else self.finalKill) - 1
            else:
                damaged = suffix
            if damaged > 0:
                self.health -= damaged #扣除血量
                print("[INFO] Oh no! You lost %d health! (%d/%d)" %\
                      (damaged,self.health,self.maxHealth))
                
            else:
                self.finalKill = index #成功击杀怪物,但是你的武器会扣除伤害
                print("[INFO] You successfully kill the monster!")
                print("[INFO] But your weapon can only kill monsters below the value of %d" % suffix)
                
        
        if prefix == 2: #桃心代表加血
            self.health = min(self.maxHealth,self.health + suffix)
            print("[INFO] You meet %s and restored %d health! (%d/%d)" %\
                 (self.card.card2str(index),suffix,self.health,self.maxHealth))
            
        
        if prefix == 0: #方块代表武器
            print("[INFO] You find the new weapon %s!" % self.card.card2str(index))
            self.finalKill = 0
            self.weapon = index  #重新装配武器
    
    def _getScore(self,index) -> int:
        if index == 1:
            index = self.card.pop()
            prefix = self.card.getPrefix(index)
            suffix = self.card.getNumber(index) + 1
            if prefix == 2:
                return self.health + suffix
            else:
                self._action(index)
                return self.health
        else:
            score = self.health
            index = self.card.pop()
            while(index != 0):
                prefix = self.card.getPrefix(index)
                suffix = self.card.getNumber(index) + 1
                score -= suffix if prefix %2 == 1 else 0 #减去所有怪物的伤害
                index = self.card.pop()
            for index in self.room:
                if index == 0: continue
                prefix = self.card.getPrefix(index)
                suffix = self.card.getNumber(index) + 1
                score -= suffix if prefix %2 == 1 else 0 #减去所有怪物的伤害
            return score
    
    def step(self) -> int:
        result = self._isEnd() #判断游戏是否结束
        if result != 0:
            score = self._getScore(result)
            print("[INFO] %s Score: %d" %\
                  ("Victory!" if result == 1 else "Defeated.",score))
            
            os.rename(self.fileName,self.fileName + "=Score%d" % score)
            return 1 
        
        self._updateRoom()
        ans = -1
        while(ans == -1):
            os.system("cls")
            print(self)
            time.sleep(0.2)
            actions = input( "\n1-4: Choose card (e.g. \"1 2 4\") \n0: Switch room\n"+\
                       "\nPlease input the number to choose your actions:")
            ans = self._decode(actions)
            
        
        if self.actions == 0:
            self._switchRoom()
            print("[INFO] You switch the room!")
            input("Press \"Enter\" key to continute")
            return 0
        
        for action in self.actions:
            if self.health <= 0:
                return 0
            self._action(action)
            self.room[action - 1] = 0
            time.sleep(0.2)
        self._step += 1
        self._isSwitch = False
        input("Press \"Enter\" key to continute")
        return 0
        
if __name__ == "__main__":
    game = Game()
    try:
        while(game.step() == 0):
            pass
    except KeyboardInterrupt:
        os.system("cls") #清屏不给看
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值