#!/usr/bin/env python3
#
# Testing tool for the Dungeon of Darkness problem
#
# Usage:
#
#   python3 testing_tool.py -f inputfile <program invocation>
#
#
# Use the -f parameter to specify the input file, e.g. 1.in.
# Format of the input file:
# - One line with three integers: the number of doors (n), the ID of the first room, and the ID of the last room.
# - n lines with three integers, for each door: the symbol ID on the door and the IDs of the two rooms that it connects.
# e.g.:
# 3 1 4
# 1 1 2
# 2 1 3
# 3 3 4
#
#
# You can compile and run your solution as follows:

# C++:
#   g++ solution.cpp
#   python3 testing_tool.py -f 1.in ./a.out

# Python3
#   python3 testing_tool.py -f 1.in python3 ./solution.py

# Java
#   javac solution.java
#   python3 testing_tool.py -f 1.in java solution

# Kotlin
#   kotlinc solution.kt
#   python3 testing_tool.py -f 1.in kotlin solutionKt


# The tool is provided as-is, and you should feel free to make
# whatever alterations or augmentations you like to it.
#
# The tool attempts to detect and report common errors, but it is not an
# exhaustive test. It is not guaranteed that a program that passes this testing
# tool will be accepted.


import argparse
import subprocess
import traceback


def write(p, line):
    assert p.poll() is None, "Program terminated early"
    print(f"Write: {line}", flush=True)
    p.stdin.write(f"{line}\n")
    p.stdin.flush()


def read(p):
    assert p.poll() is None, "Program terminated early"
    line = p.stdout.readline().strip()
    assert line != "", "Read empty line or closed output pipe"
    print(f"Read: {line}", flush=True)
    return line


parser = argparse.ArgumentParser(description="Testing tool for problem Dungeon of Darkness.")
parser.add_argument(
    "-f",
    dest="inputfile",
    metavar="inputfile",
    default=None,
    type=argparse.FileType("r"),
    required=True,
    help="The input file to use.",
)
parser.add_argument("program", nargs="+", help="Invocation of your solution")

args = parser.parse_args()

doors = {}  # For each door, the two rooms that it connects
rooms = [set() for _ in range(1005)]  # For each room, a set of doors
with args.inputfile as f:
    lines = f.readlines()
    assert len(lines) > 0
    n_doors, first_room, final_room = map(int, lines[0].split())
    assert 1 <= n_doors <= 1000
    assert 1 <= first_room <= 1000
    assert 1 <= final_room <= 1000
    for i in range(n_doors):
        door_number, room1, room2 = map(int, lines[i + 1].split())
        doors[door_number] = (room1, room2)
        rooms[room1].add(door_number)
        rooms[room2].add(door_number)

with subprocess.Popen(
    " ".join(args.program),
    shell=True,
    stdout=subprocess.PIPE,
    stdin=subprocess.PIPE,
    universal_newlines=True,
) as p:
    try:
        write(p, n_doors)
        current_room = first_room
        doors_gone_through = 0
        while current_room != final_room:
            write(p, len(rooms[current_room]))
            write(p, " ".join(str(d) for d in rooms[current_room]))
            door_picked = int(read(p))
            assert door_picked in rooms[current_room], f"Door {door_picked} is not accessible from the current room."
            if doors.get(door_picked)[0] == current_room:
                current_room = doors.get(door_picked)[1]
            else:
                current_room = doors.get(door_picked)[0]
            doors_gone_through += 1
            if doors_gone_through > n_doors * 5:
                raise Exception("solution went through over 5*n doors")

        assert p.stdout.readline() == "", "Your submission printed extra data after finding a solution."
        assert p.wait() == 0, "Your submission did not exit cleanly after finishing."

        assert (
            doors_gone_through <= 5 * n_doors
        ), f"Gone through {doors_gone_through} doors, which is more than the allowed 5 * n = {5 * n_doors}."

        print(f"\nSuccess.\nDoors gone through: {doors_gone_through}.")

    except AssertionError as e:
        print()
        print(f"Error: {e}")
        print()
        try:
            p.wait(timeout=2)
        except subprocess.TimeoutExpired:
            print("Killing your submission after 2 second timeout.")
            p.kill()

    except Exception as e:
        print()
        traceback.print_exc()
        print()
        try:
            p.wait(timeout=2)
        except subprocess.TimeoutExpired:
            print("Killing your submission after 2 second timeout.")
            p.kill()
        raise e

    finally:
        print(f"Exit code: {p.wait()}\n", flush=True)
