Forked from
Configs / EC327 / Homeworks / Homework Three
17 commits behind the upstream repository.
-
Ari Trachtenberg authoredAri Trachtenberg authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ttt.cpp 6.17 KiB
//
// Created by Ari on 10/1/24.
//
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <map>
#include "HttpConnect.h"
#include "ttt.h"
// HELPERS
// trim whitespace, based on ChatGPT
std::string trim(const std::string& str) {
size_t start = str.find_first_not_of(" \t\n\r\f\v");
size_t end = str.find_last_not_of(" \t\n\r\f\v");
return (start == std::string::npos || end == std::string::npos) ? "" : str.substr(start, end - start + 1);
}
// Function to remove surrounding quotes from a string
std::string removeQuotes(const std::string& str) {
if (str.front() == '"' && str.back() == '"') {
return str.substr(1, str.size() - 2);
}
return str;
}
// Basic JSON parser for flat key-value pairs, based on ChatGPT
map<string, string> parseJson(const std::string& jsonString) {
map<string, string> result;
string trimmedJson = trim(jsonString);
// Remove the surrounding curly braces
if (trimmedJson.front() == '{' && trimmedJson.back() == '}') {
trimmedJson = trimmedJson.substr(1, trimmedJson.size() - 2);
} else {
throw runtime_error("Cannot decode JSON: "+jsonString);
}
stringstream ss(trimmedJson);
string item;
while (getline(ss, item, ',')) {
size_t colonPos = item.find(':');
if (colonPos == std::string::npos) {
// ignore invalid key-value pair
continue;
}
std::string key = trim(item.substr(0, colonPos));
std::string value = trim(item.substr(colonPos + 1));
key = removeQuotes(key); // Remove surrounding quotes from key
value = removeQuotes(value); // Remove surrounding quotes from value if it's a string
result[key] = value;
}
return result;
}
// parses an HTTP response and returns the body of the response, based on ChatGPT
string parseHttpResponse(const string &httpResponse) {
std::istringstream responseStream(httpResponse);
std::string line;
// 1. Parse the Status Line
std::string httpVersion;
int statusCode;
std::string statusMessage;
// Get the first line (status line)
std::getline(responseStream, line);
istringstream statusLineStream(line);
statusLineStream >> httpVersion >> statusCode;
std::getline(statusLineStream, statusMessage);
// std::cout << "HTTP Version: " << httpVersion << std::endl;
// std::cout << "Status Code: " << statusCode << std::endl;
// std::cout << "Status Message: " << statusMessage << std::endl;
// 2. Parse Header
// map<string, std::string> headers;
while (std::getline(responseStream, line) && line != "\r") {
// if (line.find(":") != std::string::npos) {
// std::string headerName = line.substr(0, line.find(":"));
// std::string headerValue = line.substr(line.find(":") + 2); // Skip the ": " characters
// headers[headerName] = headerValue;
// }
}
// std::cout << "\nHeaders:\n";
// for (const auto& header : headers) {
// std::cout << header.first << ": " << header.second << std::endl;
// }
// 3. Parse Body (if any)
string body;
getline(responseStream, body, '\0'); // Read the rest of the content as the body
return body;
}
// URL-encode a given string, based on ChatGPT
string url_encode(const string &value) {
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (char c: value) {
// Alphanumeric characters don't need to be escaped
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
} else {
// Escape any other character
escaped << '%' << std::setw(2) << (int) (unsigned char) c;
}
}
return escaped.str();
}
/**
* Run a script on the server
* @param script The script to request.
* @return The Http response.
*/
string requestTTT(string script) {
string answer =
parseHttpResponse(HttpConnect::makeRequest("agile.bu.edu", "/backend/web/" + script, "8013"));
return answer;
}
int strToInt(string theStr) {
try {
return stoi(theStr);
}
catch (invalid_argument e) { return -1; }
catch (out_of_range e) { return -1; }
}
// IMPLEMENTATIONS
PLAYER_CODE newPlayer(string name) {
return requestTTT("newPlayer.pl?name="+name);
}
string getName(PLAYER_CODE thePlayerCode) {
return requestTTT("lookupCode.pl?playerCode=" + thePlayerCode);
}
string getPlayerStatus(PLAYER_CODE thePlayerCode) {
return requestTTT("playerStatus.pl?playerCode=" + thePlayerCode);
}
string getTopRatings() {
return requestTTT("topRatings.pl");
}
GAME_CODE newGame(PLAYER_CODE xPlayerCode, PLAYER_CODE yPlayerCode) {
string gameCode = requestTTT("newGame.pl");
// set X and Y players
requestTTT("changeGameStatus.pl?gameCode=" + gameCode
+ "&setX=" + xPlayerCode
+ "&setY=" + yPlayerCode);
return gameCode;
}
SQUARE getWhoToPlay(GAME_CODE theGameCode) {
return requestTTT("getGameSide.pl?gameCode="+theGameCode)[0];
}
string getModTime(GAME_CODE theGameCode) {
return requestTTT("getModeType.pl?gameCode=" + theGameCode);
}
string getGameStatus(GAME_CODE theGameCode) {
return requestTTT("gameStatus.pl?gameCode=" + theGameCode);
}
map<string,string> parseGameStatus(GAME_CODE theGameCode) {
return parseJson(requestTTT("gameStatus.pl?gameCode=" + theGameCode));
}
string makeMove(GAME_CODE theGameCode, PLAYER_CODE thePlayerCode, SQUARE theSide, int row, int col) {
return requestTTT("changeGameStatus.pl?gameCode=" + theGameCode
+ "&" + (theSide == 'x' ? "makeMoveX" : "makeMoveY")
+ "&playerCode=" + thePlayerCode
+ "&row=" + to_string(row)
+ "&col=" + to_string(col)
);
}
int getRows(GAME_CODE theGameCode) {
return strToInt(parseGameStatus(theGameCode)["rows"]);
}
int getCols(GAME_CODE theGameCode) {
return strToInt(parseGameStatus(theGameCode)["cols"]);
}
int getInARow(GAME_CODE theGameCode) {
return strToInt(parseGameStatus(theGameCode)["inARow"]);
}
string getBoard(GAME_CODE theGameCode) {
return parseGameStatus(theGameCode)["board"];
}
SQUARE getBoardSquare(string board, int theRow, int theCol) {
int cols = board.find("\\n");
return board[theRow*(cols+2)+theCol];
}
SQUARE getHasWon(GAME_CODE theGameCode) {
return parseGameStatus(theGameCode)["hasWon"][0];
}
SQUARE getToMove(GAME_CODE theGameCode) {
return parseGameStatus(theGameCode)["toMove"][0];
}