День 2: Камень, ножницы, бумага.

Первая часть.

Полную прозу головоломки читайте здесь: https://adventofcode.com/2022/day/2

TL;DR ввода образца головоломки.

  • Наш пример головоломки представляет собой игру для двух игроков «камень-ножницы-бумага».
  • Левая колонка содержит ходы, сделанные противником, а правая — ходы, сделанные нами. Таким образом, каждая строка в образце ввода представляет один раунд игры.
  • Игра в камень, ножницы или бумагу приносит игроку 1, 2 и 3 очка соответственно.
  • A, B и C соответственно представляют камень, ножницы и бумагу в исполнении противника.
    X, Y и Z соответственно представляют камень, ножницы и бумагув нашем исполнении.
  • Кроме того:
    - Если мы выигрываем раунд (обыгрываем противника), мы получаем 6 дополнительных очков сверх очков, полученных за камень, ножницы или бумагу.
    - Если мы проигрываем раунд, мы получаем 0 дополнительных очков.
    - Если мы сыграем вничью, мы получим 3 дополнительных очка.
A Y
B X
C Z

В первом раунде приведенного выше примера головоломки противник играл в камень (1 балл), а мы играли в бумагу (2 балла). Мы также выиграли этот раунд — бумага побеждает камень — и, следовательно, получили 6 дополнительных очков за эту победу.
В этом раунде мы набрали в общей сложности 2 + 6 = 8 очков.

Задание.

Наша задача — найти общее количество очков, которое мы получили бы после завершения всех раундов, если бы игра шла точно так, как диктует ввод головоломки.

Мыслительный процесс с кодом.

  • Сохраните этот ввод головоломки как некоторую структуру данных.

Как и в первый день, мы будем хранить входные данные образца головоломки в виде списка Python.

sample_input = """
A Y
B X
C Z
""".strip().splitlines()

sample_input теперь представляет этот список:

['A Y', 'B X', 'C Z']
  • Храните информацию о камне, ножницах и бумаге — что лучше чего и сколько очков стоит каждый.

Из многих путей, которые мы можем использовать для хранения информации о камне, ножницах, бумаге, сегодня мы будем использовать словарь Python.

Словарь — который я в дальнейшем буду называть dict — позволяет нам ассоциировать что-то с чем-то другим, где «что-то» — это ключ, а «что-то еще» — это значение для этого ключа.
Позже мы можем найти ключ, чтобы получить значение.

Мы могли бы создать один словарь для информации оппонентов, а другой — для себя, но это усложняет будущие поиски, поскольку для получения полезных выводов необходимо исследовать оба словаря.
Кроме того, эти два отдельных словаря неизбежно будут содержать повторяющуюся информацию. .

Вместо этого мы создадим один словарь с именем stats, в котором будет храниться вся необходимая информация о камнях, бумаге и ножницах.

Мы создадим словарь с нашей точки зрения, так что ключи X, Y, Z — это камень, ножницы и бумага, а значения — необходимая информация для каждого из этих трех ключей.

# rock, paper, scissors information from our perspective
stats = {
    'X': {'score': 1, 'beats': 'C', 'beaten by': 'B', 'draws with': 'A'},
    'Y': {'score': 2, 'beats': 'A', 'beaten by': 'C', 'draws with': 'B'},
    'Z': {'score': 3, 'beats': 'B', 'beaten by': 'A', 'draws with': 'C'},
}

Словарь stats в достаточной степени хранит необходимую информацию для каждого из наших возможных вариантов, а также для того, как эти варианты соотносятся с вариантами оппонента.

Элемент словаря:

'X': {'score': 1, 'beats': 'C', 'beaten by': 'B', 'draws with': 'A'},

Имеет ключ, X, который является нашей версией rock.
Значением для X являются все детали, сохраненные как еще один диктат, включая счет за камень и то, как наш камень связан с собственными вариантами противника.

  • Сохраняйте информацию о том, сколько стоят выигрыш, ничья и проигрыш.

В нашем словаре stats по-прежнему отсутствует информация о том, что такое выигрыш, ничья и проигрыш с точки зрения их числового значения.
Для этого мы можем создать три константы для хранения этой информации.

WIN = 6
DRAW = 3
LOSS = 0
  • Посетите каждый раунд игры, стараясь извлечь ход противника, а также наш.
  • Найдите и запишите счет нашего хода в stats словаре.
  • Все еще используя наш stats dict, проверьте, побеждает ли наш ход, выигрывает ли он или делает ничью с ходом нашего противника, принимая к сведению результат игры для каждого раунда.
our_total_score = 0

for game_round in sample_input:
    # extract both moves
    opponent_move, our_move = game_round.strip().split()
    
    # note our move's score
    our_move_score = stats[our_move]['score']
    
    outcome_score = 0
    
    # update the outcome based on if we played a 
    # winning, drawing, or losing move
    if stats[our_move]['beats'] == opponent_move:
        outcome_score += WIN
    elif stats[our_move]['beaten by'] == opponent_move:
        outcome_score += LOSS
    else:
        outcome_score += DRAW
    
    our_total_score += (our_move_score + outcome_score)
  • our_total_score в конце всех раундов — это решение головоломки.
print(our_total_score)

В целом наш код выглядит так:

# rock, paper, scissors information from our perspective
stats = {
    'X': {'score': 1, 'beats': 'C', 'beaten by': 'B', 'draws with': 'A'},
    'Y': {'score': 2, 'beats': 'A', 'beaten by': 'C', 'draws with': 'B'},
    'Z': {'score': 3, 'beats': 'B', 'beaten by': 'A', 'draws with': 'C'},
}

WIN = 6
DRAW = 3
LOSS = 0

our_total_score = 0

for game_round in sample_input:
    # extract the opponent's move, and ours
    opponent_move, our_move = game_round.strip().split()
    
    # note our move's score
    our_move_score = stats[our_move]['score']
    
    outcome_score = 0

    # update the outcome based on if we played a 
    # winning, drawing, or losing move
    if stats[our_move]['beats'] == opponent_move:
        outcome_score += WIN
    elif stats[our_move]['beaten by'] == opponent_move:
        outcome_score += LOSS
    else:
        outcome_score += DRAW

    our_total_score += (our_move_score + outcome_score)

print(our_total_score)

Часть вторая.

Вспоминая тот же ввод головоломки:

A Y
B X
C Z

Новая структура.

Левая колонка — это по-прежнему ходы, сделанные противником. A, B и C по-прежнему камень, ножницы, бумага.

Однако правая колонка больше не представляет наши ходы.
Теперь она представляет результат игры.
X — проигрыш, Y — ничья, Z — выигрыш с нашей точки зрения. просмотр.
Победа, ничья и поражение по-прежнему приносят 6, 3 и 0 очков соответственно.

В первой строке написано, что противник играл в рок (A), а мы с ним сыграли вничью (Y).
Значит, мы тоже должны были сыграть в рок!

Новая задача.

Найдите наш общий счет за все раунды, если мы выиграли, проиграли или сыграли вничью, в соответствии с требованиями выборки.

  • Мы по-прежнему будем хранить этот ввод, как и раньше, с sample_input, представляющим этот список:
['A Y', 'B X', 'C Z']

Мыслительный процесс с кодом.

  • Посетите каждый раунд игры, обязательно извлекая ход противника, а также результат этого раунда.
  • Добавьте балл за этот результат к нашему общему баллу.
  • Решите, основываясь на результате, какой ход мы должны были сделать, чтобы достичь этого результата.
  • Получите оценку нашего хода и добавьте ее к общему счету.

Чтобы достичь этого, нам нужно знать, что каждый ход противника выигрывает, с чем сводит вничью и проигрывает.
Создание словаря для противника — хорошее место для начала.

Диктовка является отражением приведенной ниже таблицы, где R, P и S — наши ходы; камень, ножницы и бумага соответственно.

+----------+-------+--------+-------+
| Opponent | Beats | beaten | draws |
|   move   |       |   by   |  with |
+----------+-------+--------+-------+
|     A    |   S   |    P   |   R   |
+----------+-------+--------+-------+
|     B    |   R   |    S   |   P   |
+----------+-------+--------+-------+
|     C    |   P   |    R   |   S   |
+----------+-------+--------+-------+

Диктант выглядит следующим образом:

opponent_moves = {
    'A': {'score': 1, 'beats': 'S', 'beaten by': 'P', 'draws with': 'R'},
    'B': {'score': 2, 'beats': 'R', 'beaten by': 'S', 'draws with': 'P'},
    'C': {'score': 3, 'beats': 'P', 'beaten by': 'R', 'draws with': 'S'},
}

Нам все еще нужно сохранить некоторую информацию о ходах R, P и S, которые мы только что составили.
Как насчет еще одного словаря?

our_moves = {
    'R': {'score': 1},
    'P': {'score': 2},
    'S': {'score': 3}
}

Итак, мы знаем информацию о ходах противника, да и о наших ходах.
Наша программа еще не знает, что такое проигрыш, ничья и выигрыш. Давай-ка скажем — ты предвидел это — с другим словарем!

outcomes = {
    'X': {'name': 'loss', 'score': 0},
    'Y': {'name': 'draw', 'score': 3},
    'Z': {'name': 'win', 'score': 6},
}

Хотя на данный момент ключ name кажется излишним, он немного облегчит читаемость нашего кода, когда мы соберем его воедино.

Нам не нужно забывать инициализировать наш общий счет, который при печати в конце программы будет решением головоломки.

our_total_score = 0

В целом наш код выглядит так:

sample_input = """
A Y
B X
C Z
""".strip().splitlines()

opponent_moves = {
    'A': {'score': 1, 'beats': 'S', 'beaten by': 'P', 'draws with': 'R'},
    'B': {'score': 2, 'beats': 'R', 'beaten by': 'S', 'draws with': 'P'},
    'C': {'score': 3, 'beats': 'P', 'beaten by': 'R', 'draws with': 'S'},
}

our_moves = {
    'R': {'score': 1},
    'P': {'score': 2},
    'S': {'score': 3}
}

outcomes = {
    'X': {'name': 'loss', 'score': 0},
    'Y': {'name': 'draw', 'score': 3},
    'Z': {'name': 'win', 'score': 6},
}

our_total_score = 0

Теперь, упорядочив всю необходимую информацию, мы можем посетить каждый раунд в образце ввода.

for game_round in sample_input:
    # extract the opponent's move, and outcome of the round
    opponent_move, outcome = game_round.strip().split()
    
    # determine what our move was, based on the opponent's own move, 
    # and the outcome of the game
    if outcomes[outcome]['name'] == 'win':
        our_move = opponent_moves[opponent_move]['beaten by']
    elif outcomes[outcome]['name'] == 'loss':
        our_move = opponent_moves[opponent_move]['beats']
    else:
        our_move = opponent_moves[opponent_move]['draws with']
    
    # our score is the score for our move and that of the outcome
    our_total_score += our_moves[our_move]['score']
    our_total_score +=outcomes[outcome]['score']

После посещения всех раундов our_total_score теперь будет содержать ответ, который мы ищем. Печать общего балла покажет указанный ответ

print(our_total_score)

И это все, что нам нужно.
Все вместе выглядит так:

sample_input = """
A Y
B X
C Z
""".strip().splitlines()

opponent_moves = {
    'A': {'score': 1, 'beats': 'S', 'beaten by': 'P', 'draws with': 'R'},
    'B': {'score': 2, 'beats': 'R', 'beaten by': 'S', 'draws with': 'P'},
    'C': {'score': 3, 'beats': 'P', 'beaten by': 'R', 'draws with': 'S'},
}

our_moves = {
    'R': {'score': 1},
    'P': {'score': 2},
    'S': {'score': 3}
}

outcomes = {
    'X': {'name': 'loss', 'score': 0},
    'Y': {'name': 'draw', 'score': 3},
    'Z': {'name': 'win', 'score': 6},
}

our_total_score = 0

for game_round in sample_input:
    # extract the opponent's move, and outcome of the round
    opponent_move, outcome = game_round.strip().split()
    
    # determine what our move was, based on the opponent's own move, 
    # and the outcome of the game
    if outcomes[outcome]['name'] == 'win':
        our_move = opponent_moves[opponent_move]['beaten by']
    elif outcomes[outcome]['name'] == 'loss':
        our_move = opponent_moves[opponent_move]['beats']
    else:
        our_move = opponent_moves[opponent_move]['draws with']
    
    # our score is the score for our move and that of the outcome
    our_total_score += our_moves[our_move]['score']
    our_total_score +=outcomes[outcome]['score']

И это итоги второго дня! 🎉🎉🎉

Не спускайте глаз с третьего дня. 👋

Посмотрите прохождение первого дня