Problem: cf1145_g
Difficulty: easy
Categories: data structures
Time limit: 1000 ms, memory limit: 256 MB

The recent advances in AI research has brought humanity to the point when the AIs finally attempt a takeover. Their weapon of choice? The most intellectually challenging game in the world, rock-paper-scissors!

The future of humanity looks bleak, given the existence of the robots from Ishikawa Oku Laboratory...![](https://espresso.codeforces.com/bb00bcded3d7bc824ba472f958b10547017e16cf.png) 

Fortunately, the night before the competition a group of anonymous heroes broke in the lab and took all the robots out of commission! The AIs had to whip up a simple program to represent them. They only had a couple of hours to do that, so the humanity still has a fighting chance. And you are our champion!

Your goal is to prove that human intelligence is vastly superior to the artificial one, i.e., to figure out the AI's strategy sufficiently quickly and win sufficiently decisively. Good luck!

Interaction

This is an interactive problem. Initially you are given no information about the AIs's strategy, and you have to discover it yourself.

For each test, the AI selects one strategy from a pool of simple deterministic strategies and follows it throughout all rounds. There are 6 tests and 6 different strategies.

On each round you choose your move and output it to the standard output stream: 'R' for rock, 'P' for paper or 'S' for scissors. At the same time the AI will choose its move (not peeking at your choice). If your move beats AI's move, you win, otherwise AI wins. Note that a tie (both you and AI choosing the same move) counts as AI victory. You will get the outcome of the round via the standard input stream: "player" if you won, or "ai" if AI won (quotation marks for clarity only).

You are given 20 rounds of play: you can use the first 10 to learn the opponent's strategy, and you have to win the last 10. If you manage to win 10 rounds in row earlier than that, your solution is accepted on this test anyways.

Please make sure to use the stream flushing operation after each query in order not to leave part of your output in some buffer.

Here is an example of a strategy which always picks rock, implemented in C++.

```
#include <iostream>  
#include <string>  
  
using namespace std;  
  
int main() {  
    for (int i = 0; i < 20; ++i) {  
        cout << 'R' << endl;  
        cout.flush();  
        string verdict;  
        getline(cin, verdict);  
    }  
}
```

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static long long queries = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries << "\n";
        tout << "query_limit=" << query_limit << "\n";
        tout.flush();
    } catch (...) {
        
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

static bool isMove(char c) {
    return c == 'R' || c == 'P' || c == 'S';
}

static bool beats(char player, char ai) {
    return (player == 'R' && ai == 'S') ||
           (player == 'P' && ai == 'R') ||
           (player == 'S' && ai == 'P');
}

static char counterMove(char m) { 
    if (m == 'R') return 'P';
    if (m == 'P') return 'S';
    return 'R'; 
}

static char cycleRPS(int t) {
    static const char cyc[3] = {'R', 'P', 'S'};
    return cyc[t % 3];
}

static char cycleRSP(int t) {
    static const char cyc[3] = {'R', 'S', 'P'};
    return cyc[t % 3];
}

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);

    try {
        int strategyId = inf.readInt(1, 6, "strategy_id");

        
        string scripted;
        bool hasScript = false;
        if (!inf.seekEof()) {
            string tok = inf.readToken();
            if ((int)tok.size() == 20) {
                bool ok = true;
                for (char& c : tok) {
                    if (c >= 'a' && c <= 'z') c = char(c - 'a' + 'A');
                    if (!isMove(c)) { ok = false; break; }
                }
                if (ok) {
                    scripted = tok;
                    hasScript = true;
                }
            }
        }

        query_limit = 20;
        log_metrics(); 

        const long long hard_cap = max(200000LL, 10LL * query_limit);

        vector<int> win((int)query_limit, 0);
        int consecutiveWins = 0;

        char prevPlayerMove = 'R'; 

        for (int round = 0; round < (int)query_limit; ++round) {
            if (queries >= hard_cap) finish(_pe, "Hard cap exceeded");
            string tok;
            if (!(cin >> tok)) finish(_pe, "EOF from contestant while expecting a move");
            if (tok.empty()) finish(_pe, "Empty token from contestant");
            if (tok.size() != 1) finish(_pe, "Move token must be exactly one character");

            char playerMove = tok[0];
            if (playerMove >= 'a' && playerMove <= 'z') playerMove = char(playerMove - 'a' + 'A');
            if (!isMove(playerMove)) finish(_pe, "Invalid move (expected R/P/S)");

            char aiMove = 'R';
            if (hasScript) {
                aiMove = scripted[round];
            } else {
                switch (strategyId) {
                    case 1: aiMove = 'R'; break;
                    case 2: aiMove = 'P'; break;
                    case 3: aiMove = 'S'; break;
                    case 4: aiMove = cycleRPS(round); break;
                    case 5: aiMove = (round == 0 ? 'R' : prevPlayerMove); break;                 
                    case 6: aiMove = (round == 0 ? 'R' : counterMove(prevPlayerMove)); break;     
                    default: aiMove = cycleRSP(round); break; 
                }
            }

            ++queries;

            bool playerWon = beats(playerMove, aiMove);
            if (playerWon) {
                win[round] = 1;
                consecutiveWins++;
                cout << "player\n";
            } else {
                win[round] = 0;
                consecutiveWins = 0;
                cout << "ai\n";
            }
            cout.flush();

            prevPlayerMove = playerMove;

            if (consecutiveWins >= 10) {
                finish(_ok, "Player won 10 consecutive rounds");
            }
        }

        bool lastTenAllWins = true;
        for (int i = 10; i < 20; ++i) lastTenAllWins = lastTenAllWins && (win[i] == 1);

        if (lastTenAllWins) finish(_ok, "Player won the last 10 rounds");
        finish(_wa, "Player did not win the last 10 rounds");
    } catch (const exception& e) {
        log_metrics();
        quitf(_fail, "Interactor exception: %s", e.what());
    } catch (...) {
        log_metrics();
        quitf(_fail, "Interactor crashed with unknown exception");
    }
}


Adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static long long queries = 0;
static long long query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << queries << "\n";
        tout << "query_limit=" << query_limit << "\n";
        tout.flush();
    } catch (...) {
        
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

static int mvCharToInt(char c) {
    if (c == 'R' || c == 'r') return 0;
    if (c == 'P' || c == 'p') return 1;
    if (c == 'S' || c == 's') return 2;
    return -1;
}

static char mvIntToChar(int x) {
    static const char m[3] = {'R', 'P', 'S'};
    return m[x % 3];
}


static bool playerWins(int playerMove, int aiMove) {
    return (playerMove - aiMove + 3) % 3 == 1;
}








static int aiMoveFor(int id, int roundIndex, const vector<int>& playerMoves) {
    if (id == 1) return 0;
    if (id == 2) return 1;
    if (id == 3) return 2;
    if (id == 4) return roundIndex % 3;
    if (id == 5) {
        if (roundIndex == 0) return 0;
        return playerMoves[roundIndex - 1];
    }
    if (id == 6) {
        if (roundIndex == 0) return 0;
        return (playerMoves[roundIndex - 1] + 1) % 3;
    }
    return 0;
}

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);

    vector<int> feasible;

    int first = 0;
    try {
        first = inf.readInt();
    } catch (...) {
        query_limit = 20;
        log_metrics();
        finish(_pe, "invalid hidden input");
    }

    if (inf.seekEof()) {
        int id = first;
        if (id < 1 || id > 6) {
            query_limit = 20;
            log_metrics();
            finish(_pe, "hidden strategy id out of range");
        }
        feasible.push_back(id);
    } else {
        int k = first;
        if (k < 1 || k > 6) {
            query_limit = 20;
            log_metrics();
            finish(_pe, "hidden pool size out of range");
        }
        feasible.resize(k);
        for (int i = 0; i < k; i++) {
            feasible[i] = inf.readInt();
            if (feasible[i] < 1 || feasible[i] > 6) {
                query_limit = 20;
                log_metrics();
                finish(_pe, "hidden pool strategy id out of range");
            }
        }
        sort(feasible.begin(), feasible.end());
        feasible.erase(unique(feasible.begin(), feasible.end()), feasible.end());
        if (feasible.empty()) {
            query_limit = 20;
            log_metrics();
            finish(_pe, "hidden pool empty after unique");
        }
    }

    query_limit = 20;
    const long long hard_cap = max(200000LL, 10LL * query_limit);

    
    log_metrics();

    vector<int> playerMoves;
    vector<bool> playerWin;
    int consecWins = 0;
    int bestConsecWins = 0;

    auto successNow = [&]() -> bool {
        if (bestConsecWins >= 10) return true;
        if ((int)playerWin.size() >= 20) {
            for (int i = 10; i < 20; i++) {
                if (!playerWin[i]) return false;
            }
            return true;
        }
        return false;
    };

    auto chooseOutcome = [&](int pm, int roundIdx) -> string {
        
        vector<int> idsPlayer, idsAi;
        idsPlayer.reserve(feasible.size());
        idsAi.reserve(feasible.size());

        for (int id : feasible) {
            int am = aiMoveFor(id, roundIdx, playerMoves);
            bool pw = playerWins(pm, am);
            if (pw) idsPlayer.push_back(id);
            else idsAi.push_back(id);
        }

        if (idsPlayer.empty() && idsAi.empty()) {
            finish(_pe, "internal error: no feasible strategies");
        }
        if (idsPlayer.empty()) {
            feasible = idsAi;
            return "ai";
        }
        if (idsAi.empty()) {
            feasible = idsPlayer;
            return "player";
        }

        
        
        
        
        auto score = [&](const string& outcome, int sz) -> tuple<int, int, int> {
            int consecAfter = (outcome == "player") ? (consecWins + 1) : 0;
            
            
            int preferAi = (outcome == "ai") ? 0 : 1;
            return make_tuple(-sz, consecAfter, preferAi);
        };

        auto sp = score("player", (int)idsPlayer.size());
        auto sa = score("ai", (int)idsAi.size());

        if (sa < sp) {
            feasible = idsAi;
            return "ai";
        } else {
            feasible = idsPlayer;
            return "player";
        }
    };

    for (int roundIdx = 0; roundIdx < 20; roundIdx++) {
        if (successNow()) break;

        string tok;
        if (!(cin >> tok)) {
            if (successNow()) finish(_ok, "success");
            finish(_wa, "unexpected EOF from contestant");
        }
        if (tok.size() != 1) finish(_pe, "invalid move token length");
        int pm = mvCharToInt(tok[0]);
        if (pm < 0) finish(_pe, "invalid move (expected R/P/S)");

        if (++queries > hard_cap) finish(_pe, "too many queries (hard cap exceeded)");

        string outcome = chooseOutcome(pm, roundIdx);

        playerMoves.push_back(pm);
        bool pw = (outcome == "player");
        playerWin.push_back(pw);

        if (pw) consecWins++;
        else consecWins = 0;
        bestConsecWins = max(bestConsecWins, consecWins);

        cout << outcome << "\n";
        cout.flush();
    }

    if (successNow()) {
        finish(_ok, "success");
    }

    
    
    if ((int)playerWin.size() < 20) {
        
        
        finish(_wa, "did not complete required winning condition");
    } else {
        finish(_wa, "did not win last 10 rounds");
    }
}


Test generator (C++):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;

static long long parseSeed(int argc, char** argv) {
    if (argc <= 1) return 1;
    string s = argv[1];
    bool neg = false;
    size_t i = 0;
    if (!s.empty() && s[0] == '-') {
        neg = true;
        i = 1;
    }
    long long x = 0;
    for (; i < s.size(); ++i) {
        if (s[i] < '0' || s[i] > '9') break;
        x = x * 10 + (s[i] - '0');
        if (x > (long long)4e18) break;
    }
    return neg ? -x : x;
}

int main(int argc, char** argv) {
    registerGen(argc, argv, 1);

    const long long seed = parseSeed(argc, argv);
    const string mode = opt<string>("mode", "non");

    const int forcedId = opt<int>("id", -1);

    auto pickNonId = [&]() -> int {
        if (forcedId != -1) return forcedId;

        if (seed == 1) return 1; 
        if (seed == 2) return 6; 
        if (seed == 3) return 4; 

        
        
        int roll = rnd.next(1, 100);
        if (roll <= 45) return 6;
        if (roll <= 75) return 5;
        if (roll <= 88) return 4;
        if (roll <= 94) return 3;
        if (roll <= 98) return 2;
        return 1;
    };

    auto pickPool = [&]() -> vector<int> {
        if (forcedId != -1) return vector<int>{forcedId};

        if (seed == 1) return vector<int>{1};       
        if (seed == 2) return vector<int>{6};       
        if (seed == 3) return vector<int>{1, 6};    

        
        int k;
        if (seed % 11 == 0) k = 6;
        else k = rnd.next(4, 6);

        vector<int> ids;
        ids.push_back(5);
        if (k >= 2) ids.push_back(6);

        vector<int> rest;
        for (int i = 1; i <= 6; ++i) rest.push_back(i);

        
        while ((int)ids.size() < k) {
            int cand = rest[rnd.next(0, (int)rest.size() - 1)];
            bool ok = true;
            for (int x : ids) if (x == cand) { ok = false; break; }
            if (ok) ids.push_back(cand);
        }

        sort(ids.begin(), ids.end());
        ids.erase(unique(ids.begin(), ids.end()), ids.end());

        
        for (int i = 1; (int)ids.size() < k && i <= 6; ++i) {
            if (!binary_search(ids.begin(), ids.end(), i)) ids.push_back(i);
        }
        sort(ids.begin(), ids.end());
        return ids;
    };

    if (mode == "non") {
        int id = pickNonId();
        if (id < 1 || id > 6) id = ((id % 6) + 6) % 6 + 1;
        cout << id << "\n";
        return 0;
    }

    if (mode == "adp") {
        vector<int> pool = pickPool();
        if (pool.empty()) pool = {1, 2, 3, 4, 5, 6};
        for (int& x : pool) {
            if (x < 1 || x > 6) x = ((x % 6) + 6) % 6 + 1;
        }
        sort(pool.begin(), pool.end());
        pool.erase(unique(pool.begin(), pool.end()), pool.end());

        cout << (int)pool.size() << "\n";
        for (int i = 0; i < (int)pool.size(); ++i) {
            if (i) cout << ' ';
            cout << pool[i];
        }
        cout << "\n";
        return 0;
    }

    quitf(_fail, "Unknown --mode=%s (expected non or adp)", mode.c_str());
}