Предсказать выбор / ограниченный ранд

Задача, которую мне дали, - выиграть 50 раз подряд с помощью написанного самим клиентом этого RockPaperScissor-PythonServer.

import SocketServer,threading,os,string
import random, time
f = open('secret.txt')
offset = int(f.readline().strip())

choices = {
        'r': 'rock',
        'p': 'paper',
        's': 'scissors'
}

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        rnd = random.Random()
        # Initialize the random number generator to some secret value
        # Note: the value of offset is too big to guess/bruteforce you need to find a better way :)
        rnd.seed(int(time.time() + offset))
        self.request.sendall("Rock paper scissors is back\n")
        win_count = 0
        play_again = True
        while play_again:
            while win_count < 50:
                self.request.sendall("choose one [r] rock, [p] paper, [s] scissors: ")
                your_choice = self.request.recv(1024).strip()
                if not your_choice in 'rps':
                    continue
                self.request.sendall("Your choice %s\n" % choices.get(your_choice))
                my_choice = rnd.choice("rps")
                self.request.sendall("My choice %s\n" % choices.get(my_choice))
                if my_choice == your_choice:
                    self.request.sendall("Its a tie, sorry you need to win 50 times in a row, a tie is simply not good enough.\nWho ever said life was fair?\n")
                    break
                if ((my_choice == 'r' and your_choice == 'p') or 
                        (my_choice == 'p' and your_choice == 's') or 
                        (my_choice == 's' and your_choice == 'r')):
                    win_count += 1
                    self.request.sendall("Arghhh. you beat me %s times\n" % win_count)
                else:
                    self.request.sendall("You loose!\n")
                    break

            if win_count == 50:
                self.request.sendall("50 times in a row?!? are you some kind of mind reader?\n")
                return
            else:
                win_count = 0
                answer = ''
                while answer not in ('y','n'):
                    self.request.sendall("Play again? (y/n): ")
                    answer = self.request.recv(1024).strip().lower()
                    if answer == 'n':
                        return

SocketServer.TCPServer.allow_reuse_address = True
server = ThreadedTCPServer(("0.0.0.0", 1178), MyTCPHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
server.serve_forever()

Я читал в документе python random.py и на различных сайтах, что основной генератор случайных чисел, который использует случайный класс питонов (MersenneTwister), не подходит для вещей, относящихся к безопасности, потому что он предсказуем, когда злоумышленнику удается получить 624 последовательных числа.

У меня уже есть клиент, который играет 624 раза, и в каждом раунде определяет выбор сервера, преобразует его в соответствующий индекс массива в [rps] и записывает это число в файл. Таким образом, в конце концов, есть длинный файл, содержащий множество нулей, 1 и 2, как это

0
1
0
2
2
0
....

Самая важная строка в коде сервера для меня, по-видимому,

my_choice = rnd.choice("rps")

который реализован как (выдержка из random.py):

def choice(self, seq):
  """Choose a random element from a non-empty sequence."""
  return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty

Здесь я читаю, что для предсказания следующих чисел мне нужно записать 624 последовательных числа и восстановить состояние путем отмены / отмены определенных преобразований, однако я думаю, что прямой вывод rng ядра, который является плавающей точкой между [0.0, 1.0), требуется для этого...

Чтобы получить основной вывод rng из индекса последовательности, мне кажется, что мне просто нужно полностью изменить приведенный выше код функции "choice()", что было бы что-то вроде

seq_value = seq[int(core_rng_out * len(seq))]
seq_index = int(core_rng_out * len(seq))
int^-1(seq_index) = core_rng_out * len(seq)
int^-1(seq_index) / len(seq) = core_rng_out
core_rng_out = int^-1(seq_index) / 3

Выше должно быть что-то вроде решения математического уравнения для определенной переменной. Разделен на 3, потому что последовательность имеет размер 3 ("rps"), однако, что такое обратная функция питонов int(...)?!? Выше я попытался абстрактно пометить его как обратный, сделав его ^-1.

И, кроме того, возможно ли вообще получить значение rng?!?, Потому что в питоне int-doc говорит, что когда int(...) дано значение float, произойдет / может произойти некоторое усечение...?!

Или это может быть совершенно неправильный подход, и я могу побить сервер более простым способом?

1 ответ

Решение

Мне кажется, что вы можете обмануть сервер, инициировав два соединения одновременно (в течение одной секунды).

Если они инициируются в течение одной и той же секунды, случайное начальное число будет одинаковым (из-за этой строки: rnd.seed(int(time.time() + offset)), Таким образом, сервер будет генерировать одинаковые варианты для двух клиентов.

Поэтому нужно сделать так, чтобы один клиент выбрал любую (возможно, проигрышную) стратегию, записав первые 50 вариантов выбора компьютера. Затем, зная их, вы можете сыграть на другом соединении именно выигрышные ходы.

Другие вопросы по тегам