// Cloned by Madalina Triboi on 1 Dec 2023 from World "cohere chatbot api test" by Madalina Triboi
// Please leave this clone trail here.
// Code guidance from Chat with GPT Model by Dr. Mark Humphrys
// It follows the same kind of structure as to understand how to call the API.
// AI API that is being used is Cohere.
// Similar to OpenAI's APIs but it offers free API keys; thus why I picked it.
// Tutorial followed for Tic Tac Toe grid setup in p5.js
// https://www.youtube.com/watch?v=8x_omgn3IRg&ab_channel=PattVira
// Cohere API key for authentication
var COHERE_API_KEY = "E6vTBQ2D4AWDQHrv4kA0sIXekaq3BX731TMSjila";
// Variable to track whether the game has ended
let gameEnded = false;
// Array to store chat history
var chat_hist = [];
// Variable to keep track of the current player ("X" or "O")
let currentPlayer = "X";
// Set body style using jQuery
$("body").css({
margin: "25px",
padding: "12px",
"font-family": "'Poppins', sans-serif",
"font-family": "'Roboto', sans-serif",
"font-size": "125%",
color: "#4A4B44",
});
// Call the initialization function when the page is first loaded
$(document).ready(function () {
// MH edit
AB.removeRunHeader();
initializeChat();
// Initially hide the board and other elements
// Did this in order to send the initialChat to the API to explain game rules;
// And to avoid the user clicking on the grid, confusing the API.
$("#p5-container").hide();
$("#end-game").hide();
$("#cohere").hide();
});
// Function to initialize the chat with the initial prompt
function initializeChat() {
// Initial prompt message explaining game rules
const initialPrompt =
"RESET. Welcome to Tic Tac Toe! You are 'O', and I am 'X'. The game unfolds on a 3x3 grid. Your goal is to align three 'O's either horizontally, vertically, or diagonally. If the grid fills up without a winner, it's a Tie." +
"Respond with your move using coordinates like 'x y', where 'x' and 'y' are row and column numbers (starting from 0 in the top left). No extra explanations, please." +
"Don't stop sending coordinates until I stop sending you them." +
"Valid coordinates range 0 0 to 2 2. Middle of the grid is 1 1; last cell in the grid is 2 2. Let's kick off! I'll make the first move. Do you understand ?";
// Add the initial prompt to the chat history
chat_hist.push({ role: "USER", message: initialPrompt, user_name: "Player" });
// MH edit
console.log ( "MH prompt: " + initialPrompt );
console.log ( "MH chat hist: " );
console.log ( chat_hist );
// Send the initial prompt to the Cohere API
// API DOCS: https://docs.cohere.com/reference/chat
const settings = {
async: true,
crossDomain: true,
url: "https://api.cohere.ai/v1/chat",
method: "POST",
headers: {
accept: "application/json",
"content-type": "application/json",
Authorization: "BEARER " + COHERE_API_KEY,
},
processData: false,
data: JSON.stringify({
message: initialPrompt,
model: "command",
temperature: 0.3,
chat_history: chat_hist,
}),
};
// Handle the API response
$.ajax(settings)
.done(function (response) {
// MH edit
console.log( "MH response: " );
console.log ( response);
console.log( "MH response.text: " );
console.log ( response.text );
// Once we have a response, show the board. The API now knows what game we are playing.
showBoard();
$("#start").hide();
// Check if the message is a string before pushing it into the chat history
// MH edit: change response.message to response.text
if (typeof response.text === "string") {
chat_hist.push({
role: "CHATBOT",
message: response.text,
user_name: "CohereBot",
});
} else {
console.error("Invalid message:", response.text);
}
})
.fail(function (error) {
console.error("Error:", error);
$("#cohere").text(
"An error occurred while fetching the response. Check your API key.",
);
});
}
// Function to show the Tic Tac Toe board and other elements once we received an answer from the API
function showBoard() {
$("#p5-container").show();
$("#end-game").show();
$("#cohere").show();
}
// Dynamically create HTML elements for the Tic Tac Toe game
document.write(`
<!DOCTYPE HTML>
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins&family=Roboto&display=swap');
</style>
<div style='position:absolute; left: 25%; right:25%'>
<br></br>
<h2> Tic Tac Toe with Cohere AI API </h2>
<hr style="border-top: 1px dotted #76877D">
</hr>
<br></br>
<h3 id="start">Fetching API... Loading Game... </h3>
<div id="p5-container"></div>
<div id="end-game"></div>
<div id=cohere>
<h3> Cohere Says: </h3>
<p id="text"> </p>
</div>
</div>
`);
// Function to send user's input to Cohere API
function sendToCohere(input) {
// If the game has ended, do not make an API call
if (gameEnded) {
console.log("Game Ended.");
return;
}
// Put input into the chat history so the API has somewhat a clue what the next coordinates should be
chat_hist.push({ role: "USER", message: input, user_name: "Player" });
// MH edit
console.log ( "MH input: " + input );
console.log ( "MH chat hist: " );
console.log ( chat_hist );
// API DOCS: https://docs.cohere.com/reference/chat
const settings = {
async: true,
crossDomain: true,
url: "https://api.cohere.ai/v1/chat",
method: "POST",
headers: {
accept: "application/json",
"content-type": "application/json",
Authorization: "BEARER " + COHERE_API_KEY,
},
processData: false,
data: JSON.stringify({
message: input,
model: "command",
temperature: 0.0,
chat_history: chat_hist,
}),
};
// Handle the API response
$.ajax(settings)
.done(function (response) {
// MH edit
console.log( "MH response: " );
console.log ( response);
console.log( "MH response.text: " );
console.log ( response.text );
// Check if the message is a string before pushing it into the chat history
if (typeof response.text === "string") {
// Display API response for the user to see if the API is playing right and not confused
$("#text").text(`${response.text}`);
// Use a regex to find the coordinates in the response text
// Delimited by boundaries, 1 digit number separated by whitespace
let regex = /\b\d{1} \d{1}\b/;
let match = response.text.match(regex);
// If a match was found, split the match into x and y coordinates
if (match) {
let [x, y] = match[0].split(" ");
chat_hist.push({
role: "CHATBOT",
message: response.text,
user_name: "CohereBot",
});
// Draw the move on the canvas
drawMove(parseInt(y), parseInt(x), "O");
console.log(`Move coordinates: ${x} ${y}`);
} else {
console.error("No coordinates found in response:", response.text);
// Try to get an answer again by calling sendToCohere() again
sendToCohere(input);
}
} else {
console.error("Invalid message:", response.text);
}
})
.fail(function (error) {
console.error("Error:", error);
$("#text").text("An error occurred while fetching the response.");
});
}
// Set up the canvas for drawing the Tic Tac Toe grid
function setup() {
let canvas = createCanvas(500, 500);
canvas.parent("p5-container"); // Set parent to the div with id "p5-container"
// MH edit
ABWorld.canvas = canvas; // so AB screenshot can find this non-standard P5 canvas later
let cellSize = width / 3;
// Draw the grid
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
stroke(0);
noFill();
rect(i * cellSize, j * cellSize, cellSize, cellSize);
}
}
// Stop redrawing after setup
noLoop();
}
// Initialize the game board with empty cells
let board = [
["", "", ""],
["", "", ""],
["", "", ""],
];
// Function to draw a move on the canvas
function drawMove(x, y, player) {
// Check if the cell is empty before making a move
if (board[x][y] === "") {
// Update the board with the move
board[x][y] = player;
// Draw the move on the canvas
let cellSize = width / 3;
let moveX = x * cellSize + cellSize / 2;
let moveY = y * cellSize + cellSize / 2;
textSize(32);
textAlign(CENTER, CENTER);
if (player === "X") {
text("X", moveX, moveY);
winner(board);
} else if (player === "O") {
text("O", moveX, moveY);
winner(board);
}
} else {
console.error("Cell is already occupied!");
console.log(JSON.stringify(board));
// Provide a message indicating that the selected cell is already occupied
const message = `Cell at ${x} ${y} is already occupied. Analyze this grid to see where you may be able to find an empty cell ${JSON.stringify(
board,)}. Please choose a different placement.`;
sendToCohere(message);
}
}
// Function to handle mouse click events (user's move)
function mousePressed() {
// Make a move when the mouse is pressed
let cellSize = width / 3;
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
// Check if the cell is empty before making a move
if (board[i][j] === "") {
drawMove(i, j, "X");
// Send the user's move to the Cohere API
console.log(`Mouse pressed at ${j} ${i}`);
let move = `${j} ${i}`;
sendToCohere(move);
}
}
// Evaluate the winner of the Tic Tac Toe game
// Reference: https://stackoverflow.com/questions/69505095/how-to-detect-tic-tac-toe-winner-in-javascript-with-3-child-array
function winner(board) {
winners = new Set();
// Columns check
for (let i = 0; i < 3; i++) {
if (
board[0][i] !== "" &&
new Set([board[0][i], board[1][i], board[2][i]]).size === 1
) {
winners.add(board[0][i]);
}
}
// Rows check
for (let i = 0; i < 3; i++) {
if (board[i][0] !== "" && new Set(board[i]).size === 1) {
winners.add(board[i][0]);
}
}
// Diagonals check
if (
board[1][1] !== "" &&
(new Set([board[0][0], board[1][1], board[2][2]]).size === 1 ||
new Set([board[0][2], board[1][1], board[2][0]]).size === 1)
) {
winners.add(board[1][1]);
}
if (winners.size === 2) {
return "error";
}
if (winners.size === 0) {
// TIE
if (board.every((y) => y.every((z) => z))) {
gameEnded = true;
$("#end-game").html(`<h2>TIE <h2>
<p> Reload Page if you want to play again </p>`);
}
return "incomplete";
}
if (
winners.values().next().value === "X" ||
winners.values().next().value === "O"
) {
// WIN
gameEnded = true;
$("#end-game").html(`<h2>WINNER: ${winners.values().next().value} <h2>
<p> Reload Page if you want to play again </p>`);
}
}