|
| 1 | +#include "testlib.h" |
| 2 | +#include <iostream> |
| 3 | +#include <vector> |
| 4 | +#include <string> |
| 5 | +#include <algorithm> |
| 6 | + |
| 7 | +using namespace std; |
| 8 | + |
| 9 | +// Directions: 0: N, 1: E, 2: S, 3: W |
| 10 | +const int DR[] = {-1, 0, 1, 0}; |
| 11 | +const int DC[] = {0, 1, 0, -1}; |
| 12 | + |
| 13 | +int R, C; |
| 14 | +vector<string> grid; |
| 15 | +int trueR, trueC, trueDir; |
| 16 | + |
| 17 | +// Check if position (r, c) is valid (within bounds and is empty cell) |
| 18 | +bool isValid(int r, int c) { |
| 19 | + return r >= 0 && r < R && c >= 0 && c < C && grid[r][c] == '.'; |
| 20 | +} |
| 21 | + |
| 22 | +// Calculate distance to wall in the given direction |
| 23 | +int getDistanceToWall(int r, int c, int dir) { |
| 24 | + int dist = 0; |
| 25 | + int nr = r + DR[dir]; |
| 26 | + int nc = c + DC[dir]; |
| 27 | + while (isValid(nr, nc)) { |
| 28 | + dist++; |
| 29 | + nr += DR[dir]; |
| 30 | + nc += DC[dir]; |
| 31 | + } |
| 32 | + return dist; |
| 33 | +} |
| 34 | + |
| 35 | +int main(int argc, char* argv[]) { |
| 36 | + registerInteraction(argc, argv); |
| 37 | + |
| 38 | + // Read input |
| 39 | + R = inf.readInt(); |
| 40 | + C = inf.readInt(); |
| 41 | + inf.readEoln(); // Skip end of line |
| 42 | + |
| 43 | + grid.resize(R); |
| 44 | + for (int i = 0; i < R; i++) { |
| 45 | + grid[i] = inf.readToken(); |
| 46 | + } |
| 47 | + |
| 48 | + trueR = inf.readInt() - 1; // Convert to 0-based index |
| 49 | + trueC = inf.readInt() - 1; // Convert to 0-based index |
| 50 | + trueDir = inf.readInt(); |
| 51 | + |
| 52 | + // Validate hidden start position |
| 53 | + if (!isValid(trueR, trueC)) { |
| 54 | + quitf(_fail, "Hidden start position (%d, %d) is invalid!", trueR + 1, trueC + 1); |
| 55 | + } |
| 56 | + |
| 57 | + // Read standard answer |
| 58 | + string res = ans.readToken(); // "yes" or "no" |
| 59 | + int stdSteps = ans.readInt(); // Standard number of steps |
| 60 | + |
| 61 | + // Output initial grid to participant |
| 62 | + cout << R << " " << C << endl; |
| 63 | + for (int i = 0; i < R; i++) { |
| 64 | + cout << grid[i] << endl; |
| 65 | + } |
| 66 | + cout.flush(); |
| 67 | + |
| 68 | + // Initialize current position and direction |
| 69 | + int curR = trueR; |
| 70 | + int curC = trueC; |
| 71 | + int curDir = trueDir; |
| 72 | + |
| 73 | + // Participant's step counter |
| 74 | + int userSteps = 0; |
| 75 | + |
| 76 | + // Interaction loop: allow up to 2 * stdSteps steps |
| 77 | + while (userSteps <= 2 * stdSteps) { |
| 78 | + // Output distance to wall in current direction |
| 79 | + cout << getDistanceToWall(curR, curC, curDir) << endl; |
| 80 | + cout.flush(); |
| 81 | + |
| 82 | + // Read participant's command |
| 83 | + string cmd = ouf.readToken(); |
| 84 | + userSteps++; // Each command counts as one step (including final "yes"/"no") |
| 85 | + |
| 86 | + if (cmd == "left") { |
| 87 | + // Turn left (counterclockwise) |
| 88 | + curDir = (curDir + 3) % 4; |
| 89 | + } else if (cmd == "right") { |
| 90 | + // Turn right (clockwise) |
| 91 | + curDir = (curDir + 1) % 4; |
| 92 | + } else if (cmd == "step") { |
| 93 | + // Move one step forward |
| 94 | + int nr = curR + DR[curDir]; |
| 95 | + int nc = curC + DC[curDir]; |
| 96 | + if (!isValid(nr, nc)) { |
| 97 | + // Crashed into wall |
| 98 | + cout << -1 << endl; |
| 99 | + cout.flush(); |
| 100 | + double score_ratio = 0.0; |
| 101 | + double unbounded_ratio = 0.0; |
| 102 | + quitp(score_ratio, "Value: %d. Ratio: %.4f, RatioUnbounded: %.4f. Round %d: Crashed into wall at (%d, %d)", |
| 103 | + userSteps, score_ratio, unbounded_ratio, userSteps, curR + 1, curC + 1); |
| 104 | + } |
| 105 | + curR = nr; |
| 106 | + curC = nc; |
| 107 | + } else if (cmd == "yes" || cmd == "no") { |
| 108 | + // Participant claims to have found the position or determined it's impossible |
| 109 | + // Calculate score based on number of steps |
| 110 | + // Ensure denominator is not zero (handle edge case where stdSteps is 0) |
| 111 | + if (stdSteps <= 0) stdSteps = 1; |
| 112 | + |
| 113 | + double score_ratio = 0.0; |
| 114 | + double unbounded_ratio = 0.0; |
| 115 | + string msg; |
| 116 | + |
| 117 | + if (userSteps <= stdSteps) { |
| 118 | + score_ratio = 1.0; // Perfect or better than standard |
| 119 | + unbounded_ratio = 1.0; |
| 120 | + msg = "Perfect!"; |
| 121 | + } else if (userSteps >= 2 * stdSteps) { |
| 122 | + score_ratio = 0.0; // Too slow |
| 123 | + unbounded_ratio = 0.0; |
| 124 | + msg = "Too slow."; |
| 125 | + } else { |
| 126 | + // Linear interpolation: score decreases from 1.0 to 0.0 as steps increase from stdSteps to 2*stdSteps |
| 127 | + // Formula: (2*stdSteps - userSteps) / stdSteps |
| 128 | + score_ratio = (double)(2 * stdSteps - userSteps) / (double)stdSteps; |
| 129 | + unbounded_ratio = score_ratio; |
| 130 | + msg = "Suboptimal."; |
| 131 | + } |
| 132 | + |
| 133 | + // Ensure score is in [0.0, 1.0] |
| 134 | + score_ratio = max(0.0, min(1.0, score_ratio)); |
| 135 | + unbounded_ratio = max(0.0, min(1.0, unbounded_ratio)); |
| 136 | + |
| 137 | + if (cmd == "yes") { |
| 138 | + // Participant claims solution exists, read guessed position |
| 139 | + int guessR = ouf.readInt(); |
| 140 | + int guessC = ouf.readInt(); |
| 141 | + |
| 142 | + cout << -1 << endl; |
| 143 | + cout.flush(); |
| 144 | + |
| 145 | + if (res != "yes") { |
| 146 | + // Standard answer says "no" but participant says "yes" |
| 147 | + double error_score = 0.0; |
| 148 | + double error_unbounded = 0.0; |
| 149 | + quitp(error_score, "Value: %d. Ratio: %.4f, RatioUnbounded: %.4f. Standard answer is 'no' but you output 'yes'.", |
| 150 | + userSteps, error_score, error_unbounded); |
| 151 | + } else if (guessR - 1 == curR && guessC - 1 == curC) { |
| 152 | + // Correct guess |
| 153 | + quitp(score_ratio, "Value: %d. Ratio: %.4f, RatioUnbounded: %.4f. %s UserSteps=%d, StdSteps=%d", |
| 154 | + userSteps, score_ratio, unbounded_ratio, msg.c_str(), userSteps, stdSteps); |
| 155 | + } else { |
| 156 | + // Wrong position guessed |
| 157 | + double error_score = 0.0; |
| 158 | + double error_unbounded = 0.0; |
| 159 | + quitp(error_score, "Value: %d. Ratio: %.4f, RatioUnbounded: %.4f. Wrong position guessed. At (%d, %d), guessed (%d, %d)", |
| 160 | + userSteps, error_score, error_unbounded, curR + 1, curC + 1, guessR, guessC); |
| 161 | + } |
| 162 | + } else if (cmd == "no") { |
| 163 | + // Participant claims no solution exists |
| 164 | + cout << -1 << endl; |
| 165 | + cout.flush(); |
| 166 | + |
| 167 | + if (res != "no") { |
| 168 | + // Standard answer says "yes" but participant says "no" |
| 169 | + double error_score = 0.0; |
| 170 | + double error_unbounded = 0.0; |
| 171 | + quitp(error_score, "Value: %d. Ratio: %.4f, RatioUnbounded: %.4f. Standard answer is 'yes' but you output 'no'.", |
| 172 | + userSteps, error_score, error_unbounded); |
| 173 | + } else { |
| 174 | + // Correct: no solution exists |
| 175 | + quitp(score_ratio, "Value: %d. Ratio: %.4f, RatioUnbounded: %.4f. %s UserSteps=%d, StdSteps=%d", |
| 176 | + userSteps, score_ratio, unbounded_ratio, msg.c_str(), userSteps, stdSteps); |
| 177 | + } |
| 178 | + } |
| 179 | + } else { |
| 180 | + // Invalid command |
| 181 | + cout << -1 << endl; |
| 182 | + cout.flush(); |
| 183 | + double score_ratio = 0.0; |
| 184 | + double unbounded_ratio = 0.0; |
| 185 | + quitp(score_ratio, "Value: %d. Ratio: %.4f, RatioUnbounded: %.4f. Invalid command: %s", |
| 186 | + userSteps, score_ratio, unbounded_ratio, cmd.c_str()); |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + // Exceeded maximum allowed steps |
| 191 | + cout << -1 << endl; |
| 192 | + cout.flush(); |
| 193 | + double score_ratio = 0.0; |
| 194 | + double unbounded_ratio = 0.0; |
| 195 | + quitp(score_ratio, "Value: %d. Ratio: %.4f, RatioUnbounded: %.4f. Exceeded maximum allowed steps (2 * StdSteps = %d)", |
| 196 | + userSteps, score_ratio, unbounded_ratio, 2 * stdSteps); |
| 197 | + |
| 198 | + return 0; |
| 199 | +} |
0 commit comments