Skip to content
Snippets Groups Projects
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];
}