Skip to content
Snippets Groups Projects
Commit f43a1b4c authored by Bobholamovic's avatar Bobholamovic
Browse files

modified: ttk.py

parent 98fc1e4e
Branches
No related tags found
No related merge requests found
...@@ -10,6 +10,7 @@ A Python Remake of Calculator Game "The Three Kingdoms" ...@@ -10,6 +10,7 @@ A Python Remake of Calculator Game "The Three Kingdoms"
TODO: TODO:
1. Senario class 1. Senario class
2. Lords 2. Lords
3. Perhaps Action classes and callback functions can be used to decouple the classes
""" """
import os import os
import random import random
...@@ -30,14 +31,14 @@ class Enum: ...@@ -30,14 +31,14 @@ class Enum:
def __len__(self): def __len__(self):
return len(self.ele_dict) return len(self.ele_dict)
mode_id = Enum(EPIC=1, ADVENTURE=2) mode_id = Enum(EPIC=0, ADVENTURE=1)
senario_id = Enum(TEST_SENARIO=1) senario_id = Enum(TEST_SENARIO=0)
class AI(metaclass=ABCMeta): class AI(metaclass=ABCMeta):
def __init__(self): def __init__(self):
super(AI, self).__init__() super(AI, self).__init__()
@abstractmethod @abstractmethod
def get_next_action(self, others): def get_next_action_par(self, others):
pass pass
class AISim(AI): class AISim(AI):
...@@ -45,29 +46,25 @@ class AISim(AI): ...@@ -45,29 +46,25 @@ class AISim(AI):
super(AISim, self).__init__() super(AISim, self).__init__()
assert(attk_rate < 1.0) assert(attk_rate < 1.0)
self.attk_rate = attk_rate self.attk_rate = attk_rate
def get_next_action(self, others): def get_next_action_par(self, this, others):
if random.random() < self.attk_rate: if random.random() < self.attk_rate:
return Lord.actions.ATTACK, (others[random.randint(len(others))], ) return this.actions.ATTACK, (others[random.randint(0, len(others)-1)], )
return Lord.actions[random.randint(2,4)] return this.actions[random.randint(2,4)]
class Switcher: class Switcher:
def __init__(self, opt_lst, idx_lst=None): def __init__(self, opt_lst, idx_lst=None):
self.update(opt_lst, idx_lst) self.update(opt_lst, idx_lst)
def switch(self, arg): def switch(self, arg):
return self.opt_dict.get(arg) return self.opt_dict.get(arg)
def rand_switch(self):
return self.opt_dict.get(self.idx_lst[random.randint(0, len(self.idx_lst)-1)])
def update(self, opt_lst, idx_lst): def update(self, opt_lst, idx_lst):
self.opt_dict = dict(enumerate(opt_lst)) if not idx_lst else dict(zip(idx_lst, opt_lst)) if not idx_lst:
self.opt_dict = dict(enumerate(opt_lst))
class NonNegative: self.idx_lst = list(range(len(opt_lst)))
def __init__(self, init): else:
super(NonNegative, self).__init__() self.opt_dict = dict(zip(idx_lst, opt_lst))
self._data = init self.idx_lst = idx_lst
def __get__(self, instance, owner):
return self._data
def __set__(self, instance, value):
if value < 0:
raise ValueError("Negative Value")
self._data = value
class Lord: class Lord:
r""" r"""
...@@ -79,8 +76,18 @@ class Lord: ...@@ -79,8 +76,18 @@ class Lord:
"Go and chase the deer! " "Go and chase the deer! "
""" """
n_lords = 0 # A counter of the total number of lords # n_lords = 0 # A counter of the total number of lords
lord_dict = {} # lord_dict = {}
class actions(Enum):
def __init__(self, lord):
self._action_func = (lord.attack, lord.recuperate, lord.recruit, lord.train)
for (i, act) in enumerate(self._action_func):
setattr(self, act.__name__.upper(), i)
def __len__(self):
return len(self._action_func)
def __getitem__(self, key):
return self._action_func[key]
def __init__(self, name, coin, food, troop, charm, polit, milit, fame, troop_milit, aggr): def __init__(self, name, coin, food, troop, charm, polit, milit, fame, troop_milit, aggr):
super(Lord, self).__init__() super(Lord, self).__init__()
...@@ -90,20 +97,21 @@ class Lord: ...@@ -90,20 +97,21 @@ class Lord:
self._milit = milit self._milit = milit
self._troop_milit = troop_milit self._troop_milit = troop_milit
self._fame = fame self._fame = fame
self._AI = AI(aggr) self._AI = AISim(aggr)
self.coin = coin
self.food = food
self.troop = troop
self.coin = NonNegative(coin)
self.food = NonNegative(food)
self.troop = NonNegative(troop)
self.active = True self.active = True
self.actions = actions(self) self.actions = Lord.actions(self)
def __new__(cls, *args, **kwargs): # def __new__(cls, *args, **kwargs):
if not cls.lord_dict.get(cls): # if not cls.lord_dict.get(cls):
cls.lord_dict[cls] = super(Lord, cls).__new__(cls, *args, **kwargs) # cls.lord_dict[cls] = super(Lord, cls).__new__(cls, *args, **kwargs)
cls.n_lords += 1 # cls.n_lords += 1
return cls.lord_dict[cls] # return cls.lord_dict[cls]
@property @property
def name(self): def name(self):
...@@ -125,32 +133,38 @@ class Lord: ...@@ -125,32 +133,38 @@ class Lord:
def fame(self): def fame(self):
return self._fame return self._fame
@property
def troop_milit(self):
return self._troop_milit
def recruit(self): def recruit(self):
try: troop_old = self.troop
raw = (self._fame*0.7 + self._charm*0.3)*random.random() * RECRUIT_BASE raw = (self._fame*0.7 + self._charm*0.3)*random.random() * RECRUIT_BASE
self.coin = self.coin - raw*MILITARY_PAY if self.coin < raw*MILITARY_PAY:
self.troop = self.troop + raw self.coin -= raw*MILITARY_PAY
except ValueError: self.troop += raw
else:
self.troop = self.troop + int(self.coin/MILITARY_PAY) self.troop = self.troop + int(self.coin/MILITARY_PAY)
self.coin = 0 self.coin = 0
self._troop_milit = self._troop_milit*troop_old/self.troop
def _defend(self, dmg): def _defend(self, dmg):
fame = FAME_BASE*max(self._troop, dmg) fame = FAME_BASE*max(self.troop, dmg)
try: if (self.troop > dmg) and (self._fame > fame):
self.troop = self.troop - dmg self.troop = self.troop - dmg
self._fame = self._fame - fame self._fame = self._fame - fame
except ValueError: else:
self.die() self.die()
return self.active return self.active
def attack(self, rival): def attack(self, rival):
attk, defen = (self, rival) if (random.random()>0.6) else (rival, self) attk, defen = (self, rival) if (random.random()>0.6) else (rival, self)
cs_rate = (0.001+random.random()/100) if (random.random() < 0.1) else 1 cs_rate = (0.001+random.random()/100) if (random.random() < 0.1) else 1
dmg = attk.troop * (attk._milit*0.4 + attk._troop_milit*0.6) / cs_rate dmg = attk.troop * (attk._milit*0.4 + attk._troop_milit*0.3 + attk.food/attk.troop*0.3) / cs_rate
if defen._defend(dmg): if defen._defend(dmg):
cs_rate_bk = (0.001+random.random()/100) if (random.random() < 0.2) else 1 cs_rate_bk = (0.001+random.random()/100) if (random.random() < 0.2) else 1
dmg_bk = defen.troop * (defen._milit*0.4 + defen._troop_milit*0.6) / cs_rate_bk dmg_bk = defen.troop * (defen._milit*0.2 + defen._troop_milit*0.3 + attk.food/attk.troop*0.5) / cs_rate_bk
attk._defend(dmg_bk) attk._defend(dmg_bk)
def recuperate(self): def recuperate(self):
...@@ -159,7 +173,7 @@ class Lord: ...@@ -159,7 +173,7 @@ class Lord:
def train(self): def train(self):
degree = (self._milit*0.2) if (random.random()>0.1) else (-self._milit*0.1) degree = (self._milit*0.2) if (random.random()>0.1) else (-self._milit*0.1)
self._troop_milit = self._troop_milit + degree self._troop_milit = max(1.0, self._troop_milit + degree)
def die(self): def die(self):
self.coin = 0 self.coin = 0
...@@ -167,91 +181,78 @@ class Lord: ...@@ -167,91 +181,78 @@ class Lord:
self.troop = 0 self.troop = 0
self._fame = 0 self._fame = 0
self.active = False self.active = False
self.n_lords -= 1 # self.n_lords -= 1
def next_action(self, act_id, *param): def next_action(self, act_id, *param):
act_func = self.actions[act_id] act_func = self.actions[act_id]
act_func(self, *param) act_func(*param)
return act_id, param return act_id, param
def AI_next_action(self, others): def AI_next_action(self, others):
act_id, act_param = self.AI.get_next_action_par(others) act_id, act_param = self._AI.get_next_action_par(self, others)
return self.next_action(act, *act_param) return self.next_action(act_id, *act_param)
class actions(Enum): class LiuBei(Lord):
def __init__(self, lord): def __init__(self, coin, food, troop):
_action_func = (lord.attack, lord.recuperate, lord.recruit, lord.train) super(LiuBei, self).__init__("刘备", coin, food, troop, 0.9, 0.7, 0.6, 0.9, 0.5, 0.4)
for (i, act) in enumerate(lord.actions):
setattr(actions, upper(act.__name__), i) class SunCe(Lord):
def __len__(self): def __init__(self, coin, food, troop):
return len(_action_func) super(SunCe, self).__init__("孙策", coin, food, troop, 0.7, 0.4, 0.8, 0.7, 0.6, 0.8)
def __get_item__(self, key):
return _action_func[key] class CaoCao(Lord):
def __init__(self, coin, food, troop):
super(CaoCao, self).__init__("曹操", coin, food, troop, 0.8, 0.8, 0.8, 0.6, 0.6, 0.9)
class Event(metaclass = ABCMeta): class Event(metaclass = ABCMeta):
@classmethod
@abstractproperty @abstractproperty
def info(cls): def info(self):
pass pass
@classmethod
@abstractproperty @abstractproperty
def desc(cls): def desc(self):
pass pass
@classmethod def get_disp_str(self):
def disp_str(cls): return "发生事件:{}\t{}".format(self.info, self.desc)
return "发生事件:{}\t{}".format(cls.info, cls.desc)
@classmethod
@abstractmethod @abstractmethod
def trigger(cls, obj): def trigger(self, obj):
pass pass
@classmethod def global_effect(self, obj_lst):
def global_effect(cls, obj_lst):
for obj in obj_lst: for obj in obj_lst:
cls.trigger(obj) self.trigger(obj)
class EvtDrought(Event): class EvtDrought(Event):
@classmethod
@property @property
def info(cls): def info(self):
return "天降大旱" return "天降大旱"
@classmethod
@property @property
def desc(cls): def desc(self):
return "粮食随机减少" return "粮食随机减少"
@classmethod def trigger(self, obj):
def trigger(cls, obj):
obj.food = obj.food*(1.0-random.random()/5) obj.food = obj.food*(1.0-random.random()/5)
class EvtHarvest(Event): class EvtHarvest(Event):
@classmethod
@property @property
def info(cls): def info(self):
return "大丰收" return "大丰收"
@classmethod
@property @property
def desc(cls): def desc(self):
return "粮食随机增加" return "粮食随机增加"
@classmethod def trigger(self, obj):
def trigger(cls, obj):
obj.food = obj.food*(1.0+random.random()/5) obj.food = obj.food*(1.0+random.random()/5)
class EvtPlague(Event): class EvtPlague(Event):
@classmethod
@property @property
def info(cls): def info(self):
return "瘟疫" return "瘟疫"
@classmethod
@property @property
def desc(cls): def desc(self):
return "兵士随机减员" return "兵士随机减员"
@classmethod def trigger(self, obj):
def trigger(cls, obj):
obj.troop = obj.troop*(1.0-random.random()/10) obj.troop = obj.troop*(1.0-random.random()/10)
class GlobalControl: class GlobalControl:
def __init__(self, lord_lst, player, mode=mode_id.EPIC, def __init__(self, lord_lst, evt_lst, player, mode=mode_id.EPIC):
evt_lst = (EvtDrought, EvtHarvest, EvtPlague)):
super(GlobalControl, self).__init__() super(GlobalControl, self).__init__()
self.lord_lst = lord_lst self.lord_lst = lord_lst
self.player = player self.player = player
...@@ -266,22 +267,25 @@ class GlobalControl: ...@@ -266,22 +267,25 @@ class GlobalControl:
elif n_lords_cur == 1: elif n_lords_cur == 1:
return False, "{}势力获得胜利".format(self.lord_lst[0].name) return False, "{}势力获得胜利".format(self.lord_lst[0].name)
for l in self.lord_lst:
if not l.active:
if l == self.player and self.mode == mode_id.ADVENTURE:
return False, "{}势力被消灭".format(self.player.name)
del l
return True, "" return True, ""
def check_single(self, lord):
return not lord.active
def report_event(self): def report_event(self):
idx = int(random.random()*10) evt = self.evt_switcher.rand_switch()
evt = self.evt_switcher.switch(idx) CLI.println(evt.get_disp_str())
CLI.print_ln(evt.disp_str) evt.global_effect(self.lord_lst)
evt.global_effect()
def show_action_log(self, this, act_id, param): def show_action_log(self, this, act_id, param):
if act_id == Lord.actions.ATTACK: if act_id == this.actions.ATTACK:
log_info = "{} 向 {} 发起进攻".format(this.name, self.param[0].name) log_info = "{} 向 {} 发起进攻".format(this.name, param[0].name)
if self.check_single(param[0]):
log_info += "\n{}势力被消灭了".format(param[0].name)
self.lord_lst.remove(param[0]) # Remove the dead one from the global list
if self.check_single(this):
log_info += "\n{}势力被消灭了".format(this.name)
self.lord_lst.remove(this)
else: else:
log_info = "{} {}".format(this.name, self.lord_act_desc[act_id]) log_info = "{} {}".format(this.name, self.lord_act_desc[act_id])
CLI.println(log_info) CLI.println(log_info)
...@@ -290,14 +294,14 @@ class GlobalControl: ...@@ -290,14 +294,14 @@ class GlobalControl:
for i, l in enumerate(self.lord_lst): for i, l in enumerate(self.lord_lst):
if l == self.player: if l == self.player:
act_id, param = self.get_commands() act_id, param = self.get_commands()
l.next_action(act, *param) l.next_action(act_id, *param)
else: else:
others = self.lord_lst[:i]+self.lord_lst[(i+1):] others = self.lord_lst[:i]+self.lord_lst[(i+1):]
act_id, param = l.AI_next_action(others) act_id, param = l.AI_next_action(others)
self.show_action_log(l, act_id, param) self.show_action_log(l, act_id, param)
def show_states(self): def show_states(self):
CLI.clear()
head = "编号\t姓名\t金钱\t粮食\t士兵\t声望\t军队战斗力" head = "编号\t姓名\t金钱\t粮食\t士兵\t声望\t军队战斗力"
CLI.print_info(head) CLI.print_info(head)
CLI.print_info('-'*len(head)) CLI.print_info('-'*len(head))
...@@ -312,11 +316,12 @@ class GlobalControl: ...@@ -312,11 +316,12 @@ class GlobalControl:
def get_commands(self): def get_commands(self):
self.show_cmds() self.show_cmds()
act_id = CLI.safe_input_enum("请输入指令编号:", Lord.actions, "非法指令!") act_id = CLI.safe_input_enum("请输入指令编号:", self.player.actions, "非法指令!")
if act_id == Lord.actions.ATTACK: act_param = ()
if act_id == self.player.actions.ATTACK:
while True: while True:
try: try:
act_param = CLI.safe_input_list_elem("请选择攻击对象编号:", self.lord_lst, "非法的攻击对象!") act_param = act_param+(CLI.safe_input_list_elem("请选择攻击对象编号:", self.lord_lst, "非法的攻击对象!"), )
assert(act_param is not self.player) assert(act_param is not self.player)
except: except:
CLI.print_info("不能攻击自己!") CLI.print_info("不能攻击自己!")
...@@ -330,11 +335,12 @@ class GlobalControl: ...@@ -330,11 +335,12 @@ class GlobalControl:
chk_stat, chk_str = self.check() chk_stat, chk_str = self.check()
if not chk_stat: if not chk_stat:
CLI.pause() CLI.pause()
CLI.print_ln(chk_str) CLI.println(chk_str)
return return
self.show_states() self.show_states()
self.report_event() self.report_event()
self.show_states()
self.do_next_turn() self.do_next_turn()
CLI.printed("本月结束") CLI.printed("本月结束")
...@@ -366,7 +372,7 @@ class CLI: ...@@ -366,7 +372,7 @@ class CLI:
while True: while True:
try: try:
val = int(CLI.get_command(prompt)) val = int(CLI.get_command(prompt))
assert(val >0 and val <= len(enum_cls)) assert(val >=0 and val < len(enum_cls))
except TypeError: except TypeError:
# Handling codes could and should differ for differnt error types # Handling codes could and should differ for differnt error types
CLI.print_info(err_str) CLI.print_info(err_str)
...@@ -402,15 +408,18 @@ class Senario: ...@@ -402,15 +408,18 @@ class Senario:
def show_states(self): def show_states(self):
head = "编号\t姓名\t初始金钱\t初始粮食\t初始士兵\t初始声望\t魅力\t政治能力\t军事能力\t军队战斗力" head = "编号\t姓名\t初始金钱\t初始粮食\t初始士兵\t初始声望\t魅力\t政治能力\t军事能力\t军队战斗力"
CLI.print_info(head) CLI.print_info(head)
CLI.print_info('-'*len(head))
for i, l in enumerate(self.lord_lst): for i, l in enumerate(self.lord_lst):
CLI.print_info("{no:02d}\t{name}\t{coin}\t{food}\t{troop}\t{fame}\t{charm}\t{polit}\t{milit}\t{troop_milit}" CLI.print_info("{no:02d}\t{name}\t{coin}\t{food}\t{troop}\t{fame}\t{charm}\t{polit}\t{milit}\t{troop_milit}"
.format( .format(
no=i, name=l.name, coin=l.coin, food=l.food, troop=l.troop, no=i, name=l.name, coin=l.coin, food=l.food, troop=l.troop,
fame=l.fame, charm=l.charm, polit=self.polit, milit=self.milit, troop_milit=self.troop_milit fame=l.fame, charm=l.charm, polit=l.polit, milit=l.milit, troop_milit=l.troop_milit
) )
) )
Senario.senarios[senario_id.TEST_SENARIO] = Senario((), (EvtDrought, EvtHarvest, EvtPlague)) Senario.senarios[senario_id.TEST_SENARIO] = Senario([
CaoCao(100, 1000, 1000),
SunCe(50, 800, 400),
LiuBei(10, 1000, 100)
], (EvtDrought(), EvtHarvest(), EvtPlague()))
class Game: class Game:
def __init__(self): def __init__(self):
...@@ -424,16 +433,16 @@ class Game: ...@@ -424,16 +433,16 @@ class Game:
def show_settings(self): def show_settings(self):
pass pass
def set_game(self): def set_game(self):
CLI.print_info("选择游戏模式:[1] Epic\t[2] ADVENTURE") CLI.print_info("选择游戏模式:[0] Epic\t[1] ADVENTURE")
self.mode = CLI.safe_input_enum("请输入指令编号:", mode_id, "非法指令!") self.mode = CLI.safe_input_enum("请输入指令编号:", mode_id, "非法指令!")
CLI.clear() CLI.clear()
CLI.print_info("选择剧本:[1] 测试剧本") CLI.print_info("选择剧本:[0] 测试剧本")
sen_id = CLI.safe_input_enum("请输入指令编号:", senario_id, "非法指令!") sen_id = CLI.safe_input_enum("请输入指令编号:", senario_id, "非法指令!")
CLI.clear() CLI.clear()
self.senario = Senario.senarios.get(sen_id) self.senario = Senario.senarios.get(sen_id)
self.senario.show_states() self.senario.show_states()
self.player = CLI.safe_input_list_elem("请选择势力:", self.senario.lord_lst, "非法输入!") self.player = CLI.safe_input_list_elem("请选择势力:", self.senario.lord_lst, "非法输入!")
self.gc = GlobalControl(self.senario.lord_lst, self.player, self.mode, self.senario.evt_lst) self.gc = GlobalControl(self.senario.lord_lst, self.senario.evt_lst, self.player, self.mode)
def init_game(self): def init_game(self):
self.show_welcome() self.show_welcome()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment