# room
type r {
    predicates {
        at(P, r);
        at(t, r);

        north_of(r, r);
        west_of(r, r);

        north_of/d(r, d, r);
        west_of/d(r, d, r);

        free(r, r);

        south_of(r, r') = north_of(r', r);
        east_of(r, r') = west_of(r', r);

        south_of/d(r, d, r') = north_of/d(r', d, r);
        east_of/d(r, d, r') = west_of/d(r', d, r);
    }

    rules {
        go/north :: at(P, r) & $north_of(r', r) & $free(r, r') & $free(r', r) -> at(P, r');
        go/south :: at(P, r) & $south_of(r', r) & $free(r, r') & $free(r', r) -> at(P, r');
        go/east  :: at(P, r) & $east_of(r', r) & $free(r, r') & $free(r', r) -> at(P, r');
        go/west  :: at(P, r) & $west_of(r', r) & $free(r, r') & $free(r', r) -> at(P, r');
    }

    reverse_rules {
        go/north :: go/south;
        go/west :: go/east;
    }

    constraints {
        r1 :: at(P, r) & at(P, r') -> fail();
        r2 :: at(s, r) & at(s, r') -> fail();
        r3 :: at(c, r) & at(c, r') -> fail();

        # An exit direction can only lead to one room.
        nav_rr1 :: north_of(r, r') & north_of(r'', r') -> fail();
        nav_rr2 :: south_of(r, r') & south_of(r'', r') -> fail();
        nav_rr3 :: east_of(r, r') & east_of(r'', r') -> fail();
        nav_rr4 :: west_of(r, r') & west_of(r'', r') -> fail();

        # Two rooms can only be connected once with each other.
        nav_rrA :: north_of(r, r') & south_of(r, r') -> fail();
        nav_rrB :: north_of(r, r') & west_of(r, r') -> fail();
        nav_rrC :: north_of(r, r') & east_of(r, r') -> fail();
        nav_rrD :: south_of(r, r') & west_of(r, r') -> fail();
        nav_rrE :: south_of(r, r') & east_of(r, r') -> fail();
        nav_rrF :: west_of(r, r')  & east_of(r, r') -> fail();
    }

    inform7 {
        type {
            kind :: "room";
        }

        predicates {
            at(P, r) :: "The player is in {r}";
            at(t, r) :: "The {t} is in {r}";

            north_of(r, r') :: "The {r} is mapped north of {r'}";
            south_of(r, r') :: "The {r} is mapped south of {r'}";
            east_of(r, r') :: "The {r} is mapped east of {r'}";
            west_of(r, r') :: "The {r} is mapped west of {r'}";

            north_of/d(r, d, r') :: "South of {r} and north of {r'} is a door called {d}";
            south_of/d(r, d, r') :: "North of {r} and south of {r'} is a door called {d}";
            east_of/d(r, d, r') :: "West of {r} and east of {r'} is a door called {d}";
            west_of/d(r, d, r') :: "East of {r} and west of {r'} is a door called {d}";
        }

        commands {
            go/north :: "go north" :: "going north";
            go/south :: "go south" :: "going south";
            go/east :: "go east" :: "going east";
            go/west :: "go west" :: "going west";
        }
    }
}
