Реализация паттерна состояние
Код ниже подсчитывает из строки с символами количество очков по следующей схеме:
«Х» – 20 очков
«4/» - 15 очков
«34» – сумма 3+4=7
«-4» - сумма 0+4=4
Подсчет делится на 2 хода, и определенные комбинации дают определенные очки. Ранее подсчет у меня велся с помощью функции result_count, которая брала из get_score оба хода и высчитывала.
Теперь мне нужно реализовать с помощью паттерна состояние чтобы каждый ход рассчитывался отдельно. Я читал по нему информацию, но так до конца не пойму, как можно его применить в этом коде. Я создал класс, берущий список обоих ходов, в котором есть 2 метода, рассчитывающие отдельно 1 и 2 ход путем разбивки внутри себя двух значений. Но насколько я понимаю, это не та конструкция, должно быть реализовано какое-то переключение между методами, а не так чтобы они оба вызывались. Подскажите, что тут нужно изменить для правильного решения?
analysed_res = {}
total = 0
values = []
def get_score(game_result):
global analysed_res, total
analysed_res = {}
total = 0
frames = 0
for _ in game_result:
for key, value in enumerate(zip(game_result.replace('X', 'X-')[0::2], game_result.replace('X', 'X-')[1::2]),
start=1):
analysed_res[key] = value
for number, analysed_value in analysed_res.items():
frames += 1
#result_count(analysed_value)
values.append(analysed_value)
def result_count(analysed_value):
global total
if 'X' in analysed_value:
total += 20
elif '/' in analysed_value:
total += 15
elif '-' in analysed_value:
total += 0
else:
total += int(analysed_value[0]) + int(analysed_value[1])
return analysed_value
class Switcher:
def __init__(self, value):
self.value = value
self.first_score = 0
self.second_score = 0
def first_move(self):
for value in self.value:
one = value[0]
if one == 'X':
self.first_score += 20
elif one == '-' or value[1] == '/':
pass
else:
self.first_score += int(one)
def second_move(self):
for value in self.value:
two = value[1]
if two == '/':
self.second_score += 15
elif two == '-':
pass
else:
self.second_score += int(two)
if __name__ == "__main__":
result = '123/XXXXXX--X'
get_score(game_result=result)
s = Switcher(values)
s.first_move()
s.second_move()
print(s.first_score, s.second_score)
Всего 10 кеглей. Игра состоит из 10 фреймов. В одном фрейме до 2х бросков, цель - сбить все кегли.
Результаты фрейма записываются символами:
«Х» – «strike», все 10 кеглей сбиты первым броском
«<число>/», например «4/» - «spare», в первый бросок сбиты 4 кегли, во второй – остальные
«<число><число>», например, «34» – в первый бросок сбито 3, во второй – 4 кегли.
вместо <число> может стоять прочерк «-», например «-4» - ни одной кегли не было сбито за первый бросок
Результат игры – строка с записью результатов фреймов. Символов-разделителей между фреймами нет.
Например, для игры из 4 фреймов запись результатов может выглядеть так:
«Х4/34-4»
Предлагается упрощенный способ подсчета количества очков:
«Х» – strike всегда 20 очков
«4/» - spare всегда 15 очков
«34» – сумма 3+4=7
«-4» - сумма 0+4=4
То есть для игры «Х4/34-4» сумма очков равна 20+15+7+4=46
Ответы (3 шт):
Решение с использованием паттерна состояние
from abc import ABCMeta, abstractmethod
class State(metaclass=ABCMeta):
@abstractmethod
def throw(self, throw_result):
pass
class First_attempt(State):
def __init__(self, game):
self.game = game
def throw(self, throw_result):
if throw_result == 'X':
self.game.score += 20
else:
if throw_result.isdigit():
self.game.first_attempt_points = int(throw_result)
self.game.state = self.game.second_attempt
elif throw_result == '-':
self.game.state = self.game.second_attempt
class Second_attempt(State):
def __init__(self, game):
self.game = game
def throw(self, throw_result):
if throw_result == '/':
self.game.score += 15
elif throw_result.isdigit():
self.game.score += self.game.first_attempt_points + int(throw_result)
self.game.first_attempt_points = 0
self.game.state = self.game.first_attempt
class Game:
def __init__(self):
self.first_attempt = First_attempt(self)
self.second_attempt = Second_attempt(self)
self.score = 0
self.first_attempt_points = 0
self.state = self.first_attempt
def throw(self, throw_result):
self.state.throw(throw_result)
Тестирование
scores = [
"X4/34-4",
"123/XXXXXX--X"
]
for score in scores:
game = Game()
for throw_result in score:
game.throw(throw_result)
print(game.score)
Результат
46
158
Немного другой вариант.
class Score():
score = 0
index = 0
strscore = ''
@classmethod
def add_score(cls, addscore):
cls.score += addscore
@classmethod
def add_str_score(cls, sscore):
cls.score = 0
cls.index = 0
cls.strscore = sscore
@classmethod
def inext(cls):
cls.index += 1
return cls.index
@classmethod
def idx(cls):
return cls.index
class SecondSignScore():
cc = Score()
def __new__(cls, index, score):
if index < len(cls.cc.strscore):
if cls.cc.strscore[index] == '/':
cls.cc.add_score(15)
cls.cc.inext()
else:
sc = 0 if cls.cc.strscore[index] == '-' else int(cls.cc.strscore[index])
cls.cc.inext()
cls.cc.add_score(score + sc)
return cls.cc.score
class FirstSignScore():
cc = Score()
def __new__(cls, index):
if index < len(cls.cc.strscore):
if cls.cc.strscore[index] == 'X':
cls.cc.add_score(20)
cls.cc.inext()
else:
sc = 0 if cls.cc.strscore[index] == '-' else int(cls.cc.strscore[index])
return SecondSignScore(cls.cc.inext(), sc)
return cls.cc.score
Собственно вызов решения:
Score.add_str_score('123/XXXXXX--X')
res = 0
while Score.idx() < len(Score.strscore):
res = FirstSignScore(Score.idx())
print(res)
Ответ ожидаемый. :)
Но всё же, лучше без классов и без паттерна State.
Хоть кто-то предложит и более простое решение, на мой взгляд и это довольно лакончино:
import re
def decode_score(score):
f_score = re.sub(r'-', "0", score)
f_score = re.sub(r'\d/', "/", f_score)
x_score = re.findall(r'X', f_score)
s_score = re.findall(r'/', f_score)
d_score = list(map(int, re.findall(r'\d', f_score)))
return len(x_score) * 20 + len(s_score) * 15 + sum(d_score)
if __name__ == "__main__":
result = '123/XXXXXX--X'
print(decode_score(result))
from abc import ABC, abstractmethod
class State(ABC):
@abstractmethod
def strike(self):
pass
@abstractmethod
def spare(self):
pass
@staticmethod
def count(element):
if element == '-':
return 0
if element != '0':
return int(element)
class Bowling:
def __init__(self):
self.state = None
self.move_counter = 0
self.total_score = 0
def change_state(self, state):
self.state = state
def char_state(self, element):
if element == 'X':
self.total_score += self.state.strike()
elif element == '/':
self.total_score += self.state.spare()
elif element.isdigit() or element == '-':
self.total_score += self.state.count(element)
def split(self, game_result):
frame = game_result[self.move_counter] + game_result[self.move_counter + 1] \
if game_result[self.move_counter] != 'X' else game_result[self.move_counter]
yield frame
self.move_counter += 1 if len(frame) == 2 else 0
def switch(self, game_result):
while self.move_counter < len(game_result):
frame_generation = self.split(game_result)
for frame in frame_generation:
if len(frame) == 1:
self.change_state(FirstMove())
self.char_state(frame[0])
elif frame[1] == '/' and frame[0].isdigit() and frame[0] != '0':
self.change_state(SecondMove())
self.char_state(frame[1])
else:
self.change_state(FirstMove())
self.char_state(frame[0])
self.change_state(SecondMove())
self.char_state(frame[1])
self.move_counter += 1
class FirstMove(State):
def strike(self):
return 20
def spare(self):
pass
class SecondMove(State):
def strike(self):
pass
def spare(self):
return 15