ChatGPT와 오목 두기
Chat GPT는 텍스트 기반의 데이터를 이해하고 인간과 같은 자연스러운 대답을 만들어준다. 물론 이 과정은 무수히 많은 데이터를 통계적으로 처리하여 이루어질 것이다.
Chat GPT를 너무나도 잘 쓰고 있는 입장에서 문득 들었던 의문이 있다. 과연 오목과 같은 특수적인 상황에서도 적절한 의사결정을 할 수 있을까? 이 의문을 해소하기 위해 Chat GPT와 오목을 둘 수 있는 환경을 개발해볼 계획이다.
개발 계획
1. ChatGPT API 이용
2. ChatGPT Function Calling을 통해 게임과의 인터페이스 개발
3. 게임 개발
4. 테스트
ChatGPT API
개발을 위해 ChatGPT를 이용하기 위해서는 직접 ChatGPT API를 코드를 통해 호출해야한다. ChatGPT의 API는 OpenAI API에 포함되어 있다. 그러므로 먼저 OpenAI API Key를 생성한다.
https://algorfati.tistory.com/183
API Key를 발급하였다면 다음과 같이 코드를 통해 ChatGPT API를 이용해본다.
class Program
{
static async Task Main(string[] args)
{
var httpClient = new HttpClient();
var apiKey = "API_KEY";
var url = "https://api.openai.com/v1/chat/completions";
var requestData = new
{
model = "gpt-3.5-turbo",
messages = new[]
{
new { role = "system", content = "You are a fruit expert." },
new { role = "user", content = "I want to eat fruit. Recommend something sour." }
}
};
var requestJson = JsonSerializer.Serialize(requestData);
var requestContent = new StringContent(requestJson, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiKey);
try
{
var response = await httpClient.PostAsync(url, requestContent);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
var responseData = JsonSerializer.Deserialize<object>(responseContent);
Console.WriteLine("Response data:");
Console.WriteLine(JsonSerializer.Serialize(responseData, new JsonSerializerOptions { WriteIndented = true }));
}
catch (HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ", e.Message);
}
}
}
다음과 같이 결과가 나올 것이다.
Response data:
{
"id": "chatcmpl-8tSx0uZHEW9oFExxQ3pGirDAF14Hj",
"object": "chat.completion",
"created": 1708230018,
"model": "gpt-3.5-turbo-0125",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "I recommend trying a green apple or a ripe pineapple for a sour flavor. Both of these fruits are known for their tartness and can be a refreshing option if you\u0027re in the mood for something sour. Enjoy!"
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 27,
"completion_tokens": 43,
"total_tokens": 70
},
"system_fingerprint": "fp_69829325d0"
}
ChatGPT Function Calling
오목을 두는 ChatGPT를 개발하는경우 어떤 것들이 필요할까? ChatGPT는 다음과 같은 작업들을 처리할 수 있어야 한다.
1. 오목 보드 초기화
2. 오목 보드 상태 확인
3. AI 차례 시 의사결정된 수 두기
ChatGPT로 개발하는 하는 입장에서 위와 같은 기능들을 생각하면 막막할 수 있다. API를 통해 ChatGPT에게 텍스트로 답변을 받을수는 있겠지만 그 답변의 형태는 날것의 자연어이기 때문이다. 개발자에게 있어서 포멧, 규격과 같은 인터페이스는 필수적인 부분인데 자연어에는 그런 것들을 기대할 수 없다.
이러한 이유로 ChatGPT에서는 Function Calling이라는 기능을 제공해주고 있다.
https://platform.openai.com/docs/guides/function-calling
요약하자면 Function Calling은 GPT의 request에 functions라는 파라메터에 사용자(개발자)가 등록할 함수의 시그니쳐를 등록하고, Chat GPT는 등록된 함수 목록들 중, 자신이 현재 컨텍스트에서 수행해야할 것 같은 함수를 호출해주는 기능이다. 그러므로 위에서 언급한 작업들은 각 작업을 처리하는 함수들을 개발자가 미리 구현해두고, GPT에 AddFunction을 통해 함수 리스트와 같이 요청을 줌으로써 서로의 인터페이스를 통일시킬 수 있다.
ChatGPT Function의 예제를 잘 구현해둔 오픈소스가 있다. 빠른 구현을 위해 이것을 이용한다.
https://github.com/marcel2215/functional-gpt
게임 개발
이제 ChatGPT와 겨룰 수 있는 간단한 오목 게임을 개발한다. 개발 기능은 다음과 같다.
1. 보드 초기화
2. 인공지능 턴 수행
3. 플레이어 턴 수행
4. 보드 상태 데이터 가져오기
5. 게임의 승패여부 확인
6. 게임 보드 상태 출력
using FunctionalGPT;
namespace Omok
{
public enum BoardTypes
{
Empty, White, Black
}
public enum TurnTypes
{
None, AI, Player
}
public enum WinTypes
{
None, AI, Player
}
public static class Game
{
public static BoardTypes[,] board;
public static TurnTypes whoseTurn = TurnTypes.None;
public static bool InitBoard(int width, int height)
{
Console.WriteLine($"----InitBoard width : {width} height : {height}");
if (width < 0 || height < 0 || width < 15 || height < 15)
{
return false;
}
board = new BoardTypes[height, width];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
board[i, j] = BoardTypes.Empty;
}
}
Console.WriteLine($"----InitBoard Success. width : {width} height : {height}");
return true;
}
public static bool ProcessAITurn(int x, int y)
{
Console.WriteLine($"----ProcessAITurn {x}, {y}");
if (!IsValidPosition(x, y))
{
return false;
}
if (whoseTurn == TurnTypes.Player)
{
return false;
}
SetBoard(x, y, BoardTypes.White);
whoseTurn = TurnTypes.Player;
Console.WriteLine($"----ProcessAITurn Success. {x}, {y}");
return true;
}
public static bool ProcesPlayerTurn(int x, int y)
{
Console.WriteLine($"----ProcesPlayerTurn {x}, {y}");
if (!IsValidPosition(x, y))
{
return false;
}
if (whoseTurn == TurnTypes.AI)
{
return false;
}
SetBoard(x, y, BoardTypes.Black);
whoseTurn = TurnTypes.AI;
Console.WriteLine($"----ProcesPlayerTurn Success. {x}, {y}");
return true;
}
public static TurnTypes GetWhoseTurn()
{
Console.WriteLine($"----GetWhoseTurn");
return whoseTurn;
}
public static string GetBoardState()
{
Console.WriteLine($"----GetBoardState");
string boardState = "";
int rows = board.GetLength(0);
int columns = board.GetLength(1);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
string symbol = "";
if (board[i, j] == BoardTypes.Empty)
{
symbol = "O";
}
else if (board[i, j] == BoardTypes.Black)
{
symbol = "B";
}
else
{
symbol = "W";
}
boardState += $"{symbol} ";
}
boardState += "\n";
}
return boardState;
}
public static void PrintBoardState()
{
string boardState = "";
int rows = board.GetLength(0);
int columns = board.GetLength(1);
for (int i = -1; i < rows; i++)
{
if (i < 0)
{
for (int j = -1; j < columns; j++)
{
if (j < 0)
{
Console.Write($" \t");
}
else
{
Console.Write($"{j % 10} ");
}
}
Console.WriteLine();
continue;
}
for (int j = -1; j < columns; j++)
{
if (j < 0)
{
Console.Write($"{i}\t");
continue;
}
string symbol = "";
if (board[i, j] == BoardTypes.Empty)
{
symbol = "O";
}
else if (board[i, j] == BoardTypes.Black)
{
symbol = "B";
}
else
{
symbol = "W";
}
Console.Write($"{symbol} ");
}
Console.WriteLine();
}
}
public static WinTypes CheckIfGameWin()
{
Console.WriteLine($"----CheckIfGameWin");
if (CheckIfGameWin(BoardTypes.White))
{
return WinTypes.AI;
}
if (CheckIfGameWin(BoardTypes.Black))
{
return WinTypes.Player;
}
return WinTypes.None;
}
private static void SetBoard(int x, int y, BoardTypes type)
{
board[y, x] = type;
}
private static bool IsValidPosition(int x, int y)
{
int rows = board.GetLength(0);
int columns = board.GetLength(1);
return 0 <= x && x < columns &&
0 <= y && y < rows;
}
private static bool CheckIfGameWin(BoardTypes type)
{
int rows = board.GetLength(0);
int columns = board.GetLength(1);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
if (CheckIfGameWin(type, j, i))
{
return true;
}
}
}
return false;
}
private static bool CheckIfGameWin(BoardTypes checkType, int fromX, int fromY)
{
int rows = board.GetLength(0);
int columns = board.GetLength(1);
if (CheckIfGameWin(checkType, fromX, fromY, 1, 0))
{
return true;
}
if (CheckIfGameWin(checkType, fromX, fromY, 0, 1))
{
return true;
}
if (CheckIfGameWin(checkType, fromX, fromY, 1, 1))
{
return true;
}
if (CheckIfGameWin(checkType, fromX, fromY, 1, -1))
{
return true;
}
return false;
}
private static bool CheckIfGameWin(
BoardTypes checkType, int fromX, int fromY, int dirX, int dirY)
{
for (int i = 0; i < 5; ++i)
{
int x = fromX + dirX;
int y = fromY + dirY;
if (!IsValidPosition(x, y))
{
return false;
}
if (board[y, x] != checkType)
{
return false;
}
}
return true;
}
}
class Program
{
static private void Main(string[] args)
{
var chatGPT = new ChatGPT("API_KEY", "gpt-4");
chatGPT.AddFunction(Game.InitBoard);
chatGPT.AddFunction(Game.ProcessAITurn);
chatGPT.AddFunction(Game.ProcesPlayerTurn);
chatGPT.AddFunction(Game.GetWhoseTurn);
chatGPT.AddFunction(Game.GetBoardState);
chatGPT.AddFunction(Game.CheckIfGameWin);
chatGPT.AddFunction(Game.PrintBoardState);
string systemMessage = "";
systemMessage = "You have to play game(omok) with player. " +
"You have to win this game. You can set white on the board on AI turn. " +
"Player can set black on the board on player turn. " +
"If there are 5 of the same color in a row or column or diagonal, then that he wins. " +
"You must judge the state of the board and choose the optimal position. " +
"Always check if your action or player action is valid with the return boolean of functions. " +
"Always print the board with after any action finished. " +
"Always check if ai or player win with CheckIfGameWin."
;
var conversation = new Conversation(systemMessage);
while (true)
{
var userMessage = Console.ReadLine()!;
conversation.FromUser(userMessage);
var task = chatGPT.CompleteAsync(conversation);
task.Wait();
string assistantResponse = task.Result;
Console.WriteLine(assistantResponse);
}
}
}
}
테스트 진행
출력화면
출력 영상
ChatGPT 4.0의 API요청 제한이 걸려있는 듯 하다. 제한으로 인해 완벽하게 테스트가 진행되지 못했다.
영상에서는 ChatGPT가 꽤나 의미있는 위치에 수를 두는 것 같지만 개발 과정 중 테스트할때에는 전혀 이길 생각이 없는 위치에 둘 때가 더 많았다. 조금 더 테스트해볼 필요가 있을 것 같다.
https://www.youtube.com/watch?v=dN-nSoNk8Qc
'AI' 카테고리의 다른 글
[AI] [OpenAI] API Key 받는법 (0) | 2022.04.14 |
---|