main.move_actions()   F
last analyzed

Complexity

Conditions 23

Size

Total Lines 60
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
eloc 48
nop 0
dl 0
loc 60
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like main.move_actions() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# TODO: Дописать правила игры в README
2
# TODO: Написать аукцион (вызывается, если игрок не хочет или не может выкупить Собственность)
3
# TODO: Написать управление собственностью (аукцион; строительство; залог)
4
5
import random
6
7
from termcolor import colored
8
9
import yaml
10
11
12
# Базовый класс, описывающий игровую сессию
13
class Base:
14
    def __init__(self, field):
15
        self.field = field  # Берет разметку игрового поля из файла data.py
16
        self.players = []  # Список с объектами игроков
17
        self.current_player = 0  # Номер текущего игрока
18
        self.cells = 36  # Количество ячеек на поле
19
        self.number_of_players = 0  # Количество игроков
20
        self.currency = '₩'  # Символ валюты
21
22
        self.cp = None
23
24
25
# Класс, описывающий игрока, его позицию на поле, баланс, нахождение в тюрьме
26
class Player:
27
    def __init__(self, name: str):
28
        self.base_coord = 0  # Начальная координата
29
        self.last_coord = self.base_coord  # Предыдущая координата
30
        self.cur_coord = self.base_coord  # Текущая координата
31
        self.base_balance = 1500  # Начальный баланс
32
        self.cur_balance = self.base_balance  # Текущий баланс
33
34
        self.property = {}
35
36
        self.name = name  # Отображаемое имя
37
38
        self.start_score = sum(throw_a_die())  # Начальный бросок кубика
39
        self.takes = 0  # Дублей выпало
40
41
        self.in_jail = False  # Игрок в тюрьме?
42
        self.left_in_jail = 0  # Ходов осталось провести в тюрьме
43
        self.escape_card = 0  # Карточки освобождения из тюрьмы ("Казна" или "Шанс")
44
45
46
def start_new_game():
47
    # Начало новой игры
48
    print('*********\nMONOPOLY\n*********\n')
49
    try:
50
        game.number_of_players = int(input('Введите количество игроков:\n> '))  # Спрашиваем количество игроков (ожидая
51
    # при этом целое число)
52
    except ValueError:
53
        print('Ожидается целое число')
54
        start_new_game()
55
    else:
56
        for i in range(game.number_of_players):  # Создание профилей игроков в указанном количестве
57
            game.players.append(Player(name=input(f'Введите имя игрока {i + 1}: ')))
58
        # Сортировка игроков по убыванию стартовых очков (определение порядка хода, как в классической Монополии)
59
        for i in range(len(game.players)):
60
            # Исходно считаем наибольшим первый элемент
61
            index = i
62
            # Этот цикл перебирает несортированные элементы
63
            for j in range(i + 1, len(game.players)):
64
                if game.players[j].start_score > game.players[index].start_score:
65
                    index = j
66
            # Самый большой элемент меняем с первым в списке
67
            game.players[i], game.players[index] = game.players[index], game.players[i]
68
        # Вывод порядка хода
69
        print('Порядок хода игроков: ', end='')
70
        for i in range(len(game.players)):
71
            if i != len(game.players) - 1:  # Разделитель между игроками
72
                end = ', '  # Если сейчас выводится не последний игрок, тогда выведи после него запятую
73
            else:
74
                end = '\n'  # Иначе – новую строку
75
            print(f'{game.players[i].name}', end=end)
76
        # Создание алиаса для текущего игрока (зд.: первого, с индексом 0)
77
        game.cp = game.players[game.current_player]
78
79
80
def player_info():
81
    if game.cp.in_jail and game.cp.cur_coord == 10:
82
        state = ' (как заключенный)'
83
    elif not game.cp.in_jail and game.cp.cur_coord == 10:
84
        state = ' (как посетитель)'
85
    else:
86
        state = ''
87
    print('====')
88
    print(f'{colored("Ход игрока", attrs=["bold"])} {colored(game.cp.name, "magenta")}.')
89
    print(f'{colored("Состояние счёта:", attrs=["bold"])} {colored(game.cp.cur_balance, "magenta")}'
90
          f'{colored(game.currency, "magenta")}.')
91
    print(f'{colored("Позиция:", attrs=["bold"])} {colored(game.field[game.cp.cur_coord]["name"], "magenta")}'
92
          f'{colored(state, "magenta")}.')
93
    print(f'====')
94
95
96
def start_move():
97
    bankrupt = False
98
    if game.cp.cur_balance <= 0:  # Если баланс игрока меньше или равен нулю
99
        bankrupt = True  # Тогда признаем его банкротом
100
        print(colored('Вы банкрот!', 'red', attrs=['bold', 'blink']))
101
    if game.cp.in_jail:
102
        print('Вы находитесь в тюрьме и не можете ходиться.')
103
        if game.cp.escape_card:  # Если карточек нет (то есть это свойство равно 0), то условие не сработает
104
            print('Вы можете использовать карточки побега.')  # Можно получить среди карточек "Казна" или "Шанс"
105
            if prompt('Использовать?'):
106
                game.cp.escape_card -= 1
107
                game.cp.in_jail = False
108
                game.cp.left_in_jail = 0
109
                start_move()
110
        else:
111
            game.cp.left_in_jail -= 1  # Вычитаем один ход из его заключения
112
            if game.cp.left_in_jail != 0:  # Если заключение еще не прошло
113
                print(f'Вам осталось провести в тюрьме {game.cp.left_in_jail} хода/ов.')
114
                complete_move()
115
            else:  # Если время заключения прошло
116
                print('Вы вышли из тюрьмы.')
117
                game.cp.in_jail = False  # Освобождаем игрока
118
                game.cp.left_in_jail = 0
119
                start_move()  # И даем возможность сходиться
120
    else:
121
        print('Выберите действие:')
122
        if not bankrupt:  # И ограничиваем действия
123
            print('1 – Бросить кубик')
124
        print('2 – Управление недвижимостью')
125
        print('3 – Признать банкротство')
126
        try:
127
            move = int(input('> '))
128
        except ValueError:
129
            print('Ожидается ввод числа')
130
            start_move()
131
        else:
132
            if not bankrupt:
133
                if move in [1, 2, 3]:
134
                    if move == 1:  # Бросить кубики и завершить ход
135
                        make_move()
136
                        complete_move()
137
                    elif move == 2:  # Начать управление недвижимостью
138
                        manage_property()
139
                        start_move()
140
                    elif move == 3:  # Сдаться
141
                        # Сдаёмся и удаляем профайл игрока
142
                        recognize_bankruptcy()
143
                else:  # Неизвестная команда
144
                    print('Введите число от 1 до 3!')
145
                    start_move()
146
            else:
147
                if move in [2, 3]:
148
                    if move == 2:  # Начать управление недвижимостью
149
                        manage_property()
150
                        start_move()
151
                    elif move == 3:  # Сдаться
152
                        pass
153
                else:  # Неизвестная команда
154
                    print('Введите число от 2 до 3!')
155
                    start_move()
156
157
158
def move_actions():
159
    print(f'Вы находитесь на ячейке {game.field[game.cp.cur_coord]["name"]} ('
160
          f'{game.field[game.cp.cur_coord]["category"]})')
161
    if game.field[game.cp.cur_coord]["owned_by"] is None and game.cp.cur_coord not in [0, 2, 4, 7, 10, 17, 20, 29, 34,
0 ignored issues
show
introduced by
The variable game does not seem to be defined for all execution paths.
Loading history...
162
                                                                                       36]:
163
        # Если ячейка никому не принадлежит и если ячейка не служебная (т. е. её можно купить)
164
        if prompt(f'Предприятие никому не принадлежит. Хотите купить?\n'
165
                  f'Её стоимость: {game.field[game.cp.cur_coord]["price"]}{game.currency}\n'
166
                  f'У вас есть: {game.cp.cur_balance}{game.currency}'):  # Предлагаем игроку купить эту карточку
167
            if game.field[game.cp.cur_coord]["price"] < game.cp.cur_balance:  # Если у игрока достаточно денег
168
                game.field[game.cp.cur_coord]["owned_by"] = game.current_player  # Покупаем
169
                game.cp.cur_balance -= game.field[game.players[
170
                    game.current_player].cur_coord]["price"]
171
                game.cp.property.setdefault(game.field[game.cp.cur_coord]['category'])
172
                try:
173
                    _f = bool(len(game.cp.property.setdefault(game.field[game.cp.cur_coord]['category'])))
174
                except TypeError:
175
                    game.cp.property[game.field[game.cp.cur_coord]['category']] = list()
176
                finally:
177
                    game.cp.property[game.field[game.cp.cur_coord]['category']].append(game.field[game.cp.cur_coord])
178
                print(f'Успех! Предприятие {game.field[game.cp.cur_coord]["name"]} теперь ваше!')
179
            else:
180
                print('У вас недостаточно средств для покупки этой карточки.')
181
                auction()
182
        else:
183
            auction()
184
    else:
185
        if game.field[game.cp.cur_coord]["owned_by"] is not None and game.field[game.cp.cur_coord]["owned_by"] != \
186
                game.current_player:  # Если ячейка принадлежит какому-то игроку
187
            print(f'Предприятие {game.field[game.cp.cur_coord]["name"]} принадлежит '
188
                  f'игроку '
189
                  f'{game.players[game.field[game.cp.cur_coord]["owned_by"]].name}')
190
            # Вычисляем ренту
191
            if game.field[game.cp.cur_coord]["houses"] is None and game.field[game.cp.cur_coord]["hotel"] is None:
192
                value = game.field[game.cp.cur_coord]["renta"]
193
            elif game.field[game.cp.cur_coord]["houses"] == 1 and game.field[game.cp.cur_coord]["hotel"] is None:
194
                value = game.field[game.cp.cur_coord]["one_house_renta"]
195
            elif game.field[game.cp.cur_coord]["houses"] == 2 and game.field[game.cp.cur_coord]["hotel"] is None:
196
                value = game.field[game.cp.cur_coord]["two_house_renta"]
197
            elif game.field[game.cp.cur_coord]["houses"] == 3 and game.field[game.cp.cur_coord]["hotel"] is None:
198
                value = game.field[game.cp.cur_coord]["three_house_renta"]
199
            elif game.field[game.cp.cur_coord]["houses"] == 4 and game.field[game.cp.cur_coord]["hotel"] is None:
200
                value = game.field[game.cp.cur_coord]["four_house_renta"]
201
            elif game.field[game.cp.cur_coord]["houses"] is None and game.field[game.cp.cur_coord]["hotel"] == 1:
202
                value = game.field[game.cp.cur_coord]["hotel_renta"]
203
            else:
204
                value = 0
205
            print(f'Вы должны заплатить ему ренту в размере {value}{game.currency}.')  # И переводим ее на счет хозяина
206
            game.cp.cur_balance -= value
207
            game.players[game.field[game.cp.cur_coord]["owned_by"]].cur_balance += value
208
    if game.cp.cur_coord in [2, 17]:
209
        print('chest.')
210
        get_event_card('chest')
211
    if game.cp.cur_coord in [7, 34]:
212
        print('chance.')
213
        get_event_card('chance')
214
    if game.cp.cur_coord == 29:
215
        go_to_jail()
216
        return None
217
    pay_taxes()  # Заплотить нологе
218
219
220
def complete_move():
221
    print(f'Ход игрока {game.cp.name} завершен.')
222
    game.current_player += 1
0 ignored issues
show
introduced by
The variable game does not seem to be defined for all execution paths.
Loading history...
223
    if game.current_player == game.number_of_players:
224
        game.current_player = 0
225
    game.cp = game.players[game.current_player]
226
227
228
def make_move():
229
    # Бросаем кубики
230
    f_die, s_die = throw_a_die()
231
    print(f'На кубиках выпало {f_die} и {s_die}.')
232
    # Смещаем фишку игрока
233
    game.cp.last_coord = game.cp.cur_coord
0 ignored issues
show
introduced by
The variable game does not seem to be defined for all execution paths.
Loading history...
234
    game.cp.cur_coord += f_die + s_die
235
    if game.cp.cur_coord > game.cells:
236
        game.cp.cur_coord -= game.cells
237
        if game.cp.cur_coord == 0:
238
            pay_salary(400)
239
        else:
240
            pay_salary(200)
241
    # Выполняем действия карточки
242
    move_actions()
243
    if f_die == s_die:
244
        # Выпал дубль, делаем еще один ход
245
        print(f'Какая неожиданность! У вас выпал {colored("дубль", attrs=["bold"])}. Дубль дает право на еще один ход.')
246
        game.cp.takes += 1  # Добавляем единицу к счетчику
247
        if game.cp.takes >= 3:  # Если количество дублей подряд достигло трех, то
248
            go_to_jail()  # Отправляем игрока в тюрьму
249
            return None
250
        else:  # Иначе
251
            make_move()  # Даем сходится еще раз
252
    else:  # Если цепчока дублей нарушилась, тогда обнуляем счетчик дублей
253
        game.cp.takes = 0
254
255
256
def manage_property():
257
    if not game.cp.property:
0 ignored issues
show
introduced by
The variable game does not seem to be defined for all execution paths.
Loading history...
258
        print('У вас нет предприятий в собственности!')
259
    else:
260
        for k, v in game.cp.property.items():
261
            for i in range(len(v)):
262
                t = v[i]['name']
263
                print(f'{k}: {t}')
264
265
266
def recognize_bankruptcy():
267
    # Признать банкротство
268
    if prompt('Вы действительно хотите сдаться?'):  # Подтверждение
269
        print('Серьёзно? Ну хорошо, ваше право...')
270
        print(f'Удаление игрока {game.cp.name}.chr')  # Отсылочка для знающих :)
271
        game.players.pop(game.current_player)
0 ignored issues
show
introduced by
The variable game does not seem to be defined for all execution paths.
Loading history...
272
        for i in range(len(game.field)):
273
            if game.field[i]['owned_by'] == game.current_player:
274
                game.field[i]['owned_by'] = None
275
        print('Собственность ушедшего игрока теперь принадлежит Банку.')
276
        complete_move()
277
    else:
278
        print('Не пугайте меня так!')
279
280
281
def go_to_jail():
282
    # Отправляем игрока в тюрьму
283
    print('Вы попали в тюрьму и должны пропустить три своих хода.')
284
    game.cp.takes = 0  # Сбрасываем счетчик дублей
0 ignored issues
show
introduced by
The variable game does not seem to be defined for all execution paths.
Loading history...
285
    game.cp.in_jail = True  # Логически отправляем игрока в тюрьму
286
    game.cp.left_in_jail = 3  # Начисляем ходы в заключении
287
    game.cp.cur_coord = 10  # Физически отправляем игрока в тюрьму
288
289
290
def pay_salary(value: int):
291
    # Зарплата за прохождение круга
292
    if game.cp.last_coord > game.cp.cur_coord:  # Если игрок
0 ignored issues
show
introduced by
The variable game does not seem to be defined for all execution paths.
Loading history...
293
        # закончил круг (предыдущая координата больше текущей)
294
        print(f'Вы завершили круг. Получите зарплату в размере {value}{game.currency}')
295
        game.cp.cur_balance += value
296
297
298
def pay_taxes():
299
    if game.cp.cur_coord == 4:  # Если игрок попал на ячейку "Подоходный налог"
0 ignored issues
show
introduced by
The variable game does not seem to be defined for all execution paths.
Loading history...
300
        print('Вы попали на клетку с налогами.')
301
        print(f'Подоходный налог. С вашего счета списано 200{game.currency}')
302
        game.cp.cur_balance -= 200
303
    if game.cp.cur_coord == 38:  # Если игрок попал на ячейку "Налог на роскошь"
304
        print('Вы попали на клетку с налогами.')
305
        print(f'Налог на роскошь. С вашего счета списано 100{game.currency}')
306
        game.cp.cur_balance -= 100
307
308
309
def get_event_card(_type: str):
310
    pass
311
312
313
def auction():
314
    print(colored('Аукцион!', 'yellow', attrs=['blink']))
315
316
317
def prompt(msg: str):
318
    resp = input(f'{msg} [Д/Н]:\n> ').lower()
319
    if resp == 'д':
320
        return True
321
    return False
322
323
324
def throw_a_die():
325
    # Бросить кубики
326
    return random.randint(1, 6), random.randint(1, 6)
327
328
329
if __name__ == '__main__':
330
    try:  # Пробуй
331
        field = yaml.full_load(open("field.yml", "r"))
332
        game = Base(field)  # Создать экземпляр игры
333
        start_new_game()  # Начать новую игру
334
        while len(game.players) > 1:  # Пока количество игроков больше единицы
335
            player_info()  # Выведи информацию о текущем игроке
336
            start_move()  # Начинай новый ход
337
    except KeyboardInterrupt:  # Если игра принудительно завершена на ^C
338
        print('\nИгра завершена без сохранения прогресса.')
339
    else:  # Если игра завершилась без ручной остановки
340
        print(f'Игрок {game.players[0].name} одержал победу. Поздравляем!')  # Поздравление
341