#!/usr/bin/env python3
"""Checker for the "Beaver's path" widget sample.

A widget problem has no tests: the checker is the grader, and it holds the
answer itself. It is called as

    checker input.txt output.txt answer.txt

where output.txt is the JSON the widget produced. input.txt and answer.txt are
empty for a widget problem, so this program never opens them.

Exit codes are testlib's: 0 is a full score, 1 is a wrong answer, 7 is a partial
score which is read from a "points <n>" line printed here. TEST_COST holds what
the run is worth, which is 100 for a widget problem.

Accepted answers, and what each is worth:

    either diagonal   100%
    any full row       75%
    any full column    50%
    anything else       0
"""

import json
import os
import sys

SIZE = 4


def fail(message):
    print(message)
    sys.exit(1)


def award(fraction, message):
    cost = float(os.environ.get("TEST_COST", 100))
    print(message)

    if fraction >= 1:
        sys.exit(0)

    print("points", round(cost * fraction, 6))
    sys.exit(7)


def marked(path):
    """Reads the widget's answer and returns the marked cells as {(y, x)}."""
    try:
        with open(path, encoding="utf-8") as f:
            answer = json.load(f)
    except (OSError, ValueError) as e:
        fail("the answer is not a JSON document: %s" % e)

    if not isinstance(answer, dict):
        fail("the answer is not an object")

    rows = answer.get("cells")
    if not isinstance(rows, list) or len(rows) != SIZE:
        fail("cells is not a grid of %d rows" % SIZE)

    cells = set()
    for y, row in enumerate(rows):
        if not isinstance(row, list) or len(row) != SIZE:
            fail("row %d is not %d cells wide" % (y + 1, SIZE))

        for x, cell in enumerate(row):
            if cell not in (0, 1, True, False):
                fail("cell %d of row %d is neither 0 nor 1" % (x + 1, y + 1))

            if cell:
                cells.add((y, x))

    return cells


def main():
    if len(sys.argv) < 3:
        fail("usage: %s input.txt output.txt answer.txt" % sys.argv[0])

    cells = marked(sys.argv[2])

    if not cells:
        fail("nothing is marked")

    diagonals = [
        {(i, i) for i in range(SIZE)},
        {(i, SIZE - 1 - i) for i in range(SIZE)},
    ]

    if cells in diagonals:
        award(1, "a diagonal, and nothing else")

    for y in range(SIZE):
        if cells == {(y, x) for x in range(SIZE)}:
            award(0.75, "row %d, which the beaver could swim but not the short way" % (y + 1))

    for x in range(SIZE):
        if cells == {(y, x) for y in range(SIZE)}:
            award(0.5, "column %d, which is the long way round" % (x + 1))

    fail("%d marked cells, which are not a single straight path" % len(cells))


main()
