#include "robot.h"
#include <bits/stdc++.h>
using namespace std;
/*
    boundary: state = -2
    obstacle: state = -1
    otherwise: state = colour
    n = up, s = down, w = left, e = right 
    S[0] is the state of cell (r, c).
    S[1] is the state of the cell to the west.
    S[2] is the state of the cell to the south.
    S[3] is the state of the cell to the east.
    S[4] is the state of the cell to the north.
*/
set<string> instr;
void instruction(vector<int> S, int Z, char A, bool ignore_duplicate = false) {
    string val = "";
    for (auto &el : S) val += to_string(el);
    if (instr.count(val) && !ignore_duplicate) {
        // cout << "Duplicate with: \n";
        // cout << val << "\n";
    } else {
        if (!instr.count(val)) set_instruction(S, Z, A);
        instr.insert(val);
    }
}
void solve_empty() {
    // if its entirely empty: go right while not a border on the right, then go up
    // if 
    instruction({0, -2, 0, 0, -2}, 1, 'E'); // at (0, 0): go right
    instruction({0, 1, 0, 0, -2}, 1, 'E'); // travelling along bottom: go right
    instruction({0, 1, 0, -2, -2}, 1, 'S'); // at (W, 0): go down
    instruction({0, 0, 0, -2, 1}, 1, 'S'); // travelling along side: go down
    instruction({0, 0, -2, -2, 1}, 1, 'T'); // at (W, H): terminate
}
void solve_h2() {
    // while possible, walk right
    // if not possible, go down/up
    // if at (W, H-1), go up
    // go right while possible
    for (auto &west_state : {-2, -1, 0, 1}) {
        for (auto &south_state : {-2, -1, 0, 1}) {
            for (auto &north_state : {-2, -1, 0, 1}) {
                instruction({0, west_state, south_state, 0, north_state}, 1, 'E');
            }
        }
    }
    // go down when needed
    for (auto &east_state : {-2, -1}) {
        for (auto &west_state : {-2, 1}) {
            instruction({0, west_state, 0, east_state, -2}, 1, 'S');
        }
    } 
    // go up when needed
    for (auto &east_state : {-1}) {
        for (auto &west_state : {-2, 1}) {
            instruction({0, west_state, -2, east_state, 0}, 1, 'N');
        }
    } 
    // at end
    for (auto &west_state : {-2, -1, 0, 1}) {
        for (auto &north_state : {-1, 0, 1}) {
            instruction({0, west_state, -2, -2, north_state}, 1, 'T');
        }
    }
}
const char DIR[5] = {'H', 'W', 'S', 'E', 'N'};
const int PERM[24][4] = {
    {1, 2, 3, 4}, {1, 2, 4, 3}, {1, 3, 2, 4}, {1, 3, 4, 2}, {1, 4, 2, 3}, {1, 4, 3, 2}, 
    {2, 1, 3, 4}, {2, 1, 4, 3}, {2, 3, 1, 4}, {2, 3, 4, 1}, {2, 4, 1, 3}, {2, 4, 3, 1}, 
    {3, 1, 2, 4}, {3, 1, 4, 2}, {3, 2, 1, 4}, {3, 2, 4, 1}, {3, 4, 1, 2}, {3, 4, 2, 1}, 
    {4, 1, 2, 3}, {4, 1, 3, 2}, {4, 2, 1, 3}, {4, 2, 3, 1}, {4, 3, 1, 2}, {4, 3, 2, 1}
};
#define wall(x) for (auto &x : {-2, -1})
#define wall_or_empty(x) for (auto &x : {-1, 0})
#define any(x) for (auto &x : {-2, -1, 0, 1, 2, 3, 4, 5})
#define empty_blue(x) for (auto &x : {0, 1})
#define bygo(x) for (auto &x : {1, 2, 3, 4, 5})
#define yg(x) for (auto &x : {1, 2})
#define ygo(x) for (auto &x : {2,3,4, 5})
void rotate(vector<int> S, int Z, int C) {
    set<string> seen;
    for (auto &perm : PERM) {
        vector<int> ins(5);
        ins[0] = S[0];
        ins[perm[0]] = S[1];
        ins[perm[1]] = S[2];
        ins[perm[2]] = S[3];
        ins[perm[3]] = S[4];
        char dir;
        if (C == 0) dir = 'T';
        else if (C == 5) dir = 'H';
        else dir = DIR[perm[C-1]];
        string v = ""; for (auto &el : ins) v+=to_string(el);
        if (seen.count(v)) continue;
        seen.insert(v);
        instruction(ins, Z, dir);
    }
}
void corners() {
    // (1) special cases for the corners
    // (a) 0, 0
    instruction({0, -2, -1, 0, -2}, 1, 'E'); // only right
    instruction({0, -2, 0, -1, -2}, 1, 'S'); // only down
    instruction({0, -2, 0, 0, -2}, 2, 'E'); // both options, go right
    instruction({2, -2, 0, 0, -2}, 1, 'S'); // option 1 bad, go down
    instruction({2, -2, 0, 1, -2}, 1, 'T'); // option 1 good, make blue and terminate
    instruction({2, -2, 1, 0, -2}, 1, 'T'); // option 2 good, make blue and terminate
    // (b) (W, 0) or (0, W)
    bygo(col) {
        // keep the colour, going down or right
        instruction({0, col, 0, -2, -2}, col, DIR[2]);
        instruction({0, 0, col, -2, -2}, col, DIR[1]);
        instruction({0, -2, -2, col, 0}, col, DIR[4]);
        instruction({0, -2, -2, 0, col}, col, DIR[3]);
        // reverse a yellow
        if (col == 1) continue;
        // go in opposite direction if we have a blue
        instruction({3, col, 1, -2, -2}, 1, DIR[1]);
        instruction({3, 1, col, -2, -2}, 1, DIR[2]);
        instruction({3, -2, -2, col, 1}, 1, DIR[3]);
        instruction({3, -2, -2, 1, col}, 1, DIR[4]);
        wall_or_empty(a) {
            instruction({0, col, a, -2, -2}, 0, DIR[1]);
            instruction({0, a, col, -2, -2}, 0, DIR[2]);
            instruction({3, col, a, -2, -2}, 0, DIR[1]);
            instruction({3, a, col, -2, -2}, 0, DIR[2]);
            instruction({0, -2, -2, col, a}, 0, DIR[3]);
            instruction({0, -2, -2, a, col}, 0, DIR[4]);
            instruction({3, -2, -2, col, a}, 0, DIR[3]);
            instruction({3, -2, -2, a, col}, 0, DIR[4]);
        }
    }
    // (c) (W, H)
    wall_or_empty(c) { // we can end
        instruction({0, c, -2, -2, 1}, 1, 'T');
        instruction({0, 1, -2, -2, c}, 1, 'T');
        ygo(c2) { // need to go back
            instruction({0, c, -2, -2, c2}, 1, DIR[4]);
            instruction({0, c2, -2, -2, c}, 1, DIR[1]);
        }
    }
}
void forced_movements() {
    // (2) we are carrying the previous colour with us to the only empty square we have
    wall(a) {
        rotate({0, a, -1, 0, 1}, 1, 3);
        ygo(b) rotate({0, a, -1, 0, b}, 3, 3);
    }
}
void clean_to_green() { // or orange
    // (3) undoing a forward movement with yellow colour to clean it
    wall(a) ygo(b) rotate({3, a, -1, b, 0}, 0, 3); // case 1: cleaning the yellow colour
    wall(a) ygo(b) rotate({3, a, -1, b, 1}, 1, 3); // case 2: found the answer we're setting everything blue until we reach green/orange
}
void dead_ends() {
    // (4) we hit a dead end in our search down a branch
    wall(a) {
        ygo(b) rotate({0, a, -1, -1, b}, 0, 4);
    }
}
void blue2branch() {
    // (5) our parent is blue, but we have 2 children
   
    wall(a) rotate({0, a, 1, 0, 0}, 2, 4); // case 1: we just arrived, so set to green and go down child 1
    wall(a) rotate({2, a, 1, 1, 0}, 1, 0); // case 2: we've come back from child 1 and child 1 is blue (we found the shortest path)
    wall(a) rotate({2, a, 1, 0, 0}, 1, 3); // case 3: come back from child 1, and it doesnt lead to the answer
}
void blue3branch() {
    // (6) our parent is blue, but we have 3 children
    rotate({0, 1, 0, 0, 0}, 2, 4); // case 1: just arrived, go down first preference
    rotate({2, 1, 1, 0, 0}, 1, 0); // case 2: first preference good, set to blue and terminate
    rotate({2, 1, 0, 0, 0}, 4, 3); // case 3: first path was bad, going down second path
    rotate({4, 1, 1, 0, 0}, 1, 0); // case 4: second path good, terminating
    rotate({4, 1, 0, 0, 0}, 1, 2); // case 5: second path bad, going down third path
}
void yellow2branch() {
    // (7) our parent is yellow/orange/green, but we have 2 children
    ygo(p) {
        wall(a) rotate({0, a, p, 0, 0}, 2, 4); // case 1: we just arrived, so set to green and go down child 1
        wall(a) rotate({2, a, p, 1, 0}, 1, 2); // case 2: we've come back from child 1 and child 1 is blue (we found the shortest path)
        wall(a) rotate({2, a, p, 0, 0}, 4, 3); // case 3: come back from child 1, and it doesnt lead to the answer, so set to orange and go down 2
        wall(a) rotate({4, a, p, 1, 0}, 1, 2); // case 4: come back from child 2, and it is the answer
        wall(a) rotate({4, a, p, 0, 0}, 0, 2); // case 5: come back from child 2, and it isnt the answer
    }
}
void yellow3branch() {
    // (8) our parent is yellow/green/orange, but we have 3 children
    ygo(p) {
        rotate({0, p, 0, 0, 0}, 2, 4); // case 1: just arrived, go down first preference
        rotate({2, p, 1, 0, 0}, 1, 1); // case 2: first preference good, set to blue and go to parent
        rotate({2, p, 0, 0, 0}, 4, 3); // case 3: first path was bad, going down second path
        rotate({4, p, 1, 0, 0}, 1, 1); // case 4: second path good, terminating
        rotate({4, p, 0, 0, 0}, 5, 2); // case 5: second path bad, going down third path
        rotate({5, p, 1, 0, 0}, 1, 1); // case 6: third path good: set to blue and go to parent
        rotate({5, p, 0, 0, 0}, 0, 1); // case 7: third path bad: set to empty and go to parent
    }
}
void solve_one_path() {
    // goal: simple cleaning routine (from root)
    corners();
    forced_movements();
    clean_to_green();
    dead_ends();
    blue2branch();
    blue3branch();
    yellow2branch();
    yellow3branch();
}
void program_pulibot() {
    // solve_empty();
    // solve_h2();
    solve_one_path();
}
| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... |