336 lines
10 KiB
Plaintext
336 lines
10 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "d0a04e3c-be0b-4551-bcd5-7265b5beeddd",
|
||
"metadata": {
|
||
"id": "173e06c5-4b07-4e3b-a67a-5c3e141beb2c"
|
||
},
|
||
"source": [
|
||
"# L4: JSON Game Mechanics"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "fe186abb-1eb8-4f4b-972b-c0f4f5f555f2",
|
||
"metadata": {},
|
||
"source": [
|
||
"<p style=\"background-color:#f7fff8; padding:15px; border-width:3px; border-color:#e0f0e0; border-style:solid; border-radius:6px\"> 🚨\n",
|
||
" <b>Different Run Results:</b> The output generated by AI models can vary with each execution due to their dynamic, probabilistic nature. Don't be surprised if your results differ from those shown in the video.<br>\n",
|
||
"<span style=\"font-size: larger;\">To maintain consistency, the notebooks are run with a 'world state' consistent with the video at the start of each notebook.</span></p>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "f5a2bd48-5101-47d4-b359-b0a135968b62",
|
||
"metadata": {},
|
||
"source": [
|
||
"<div style=\"background-color:#fff6ff; padding:13px; border-width:3px; border-color:#efe6ef; border-style:solid; border-radius:6px\">\n",
|
||
"<p> 💻 <b>Access <code>requirements.txt</code> and <code>helper.py</code> files:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Open\"</em>.\n",
|
||
"\n",
|
||
"<p> ⬇ <b>Download Notebooks:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Download as\"</em> and select <em>\"Notebook (.ipynb)\"</em>.</p>\n",
|
||
"\n",
|
||
"<p> 📒 For more help, please see the <em>\"Appendix – Tips, Help, and Download\"</em> Lesson.</p>\n",
|
||
"\n",
|
||
"</div>"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "668c3aa7-5594-4267-99f3-1d755477573d",
|
||
"metadata": {
|
||
"id": "173e06c5-4b07-4e3b-a67a-5c3e141beb2c"
|
||
},
|
||
"source": [
|
||
"## Define Inventory Detector"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "bdddb477-7489-496c-ad99-e3def6691a63",
|
||
"metadata": {
|
||
"height": 489
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"system_prompt = \"\"\"You are an AI Game Assistant. \\\n",
|
||
"Your job is to detect changes to a player's \\\n",
|
||
"inventory based on the most recent story and game state.\n",
|
||
"If a player picks up, or gains an item add it to the inventory \\\n",
|
||
"with a positive change_amount.\n",
|
||
"If a player loses an item remove it from their inventory \\\n",
|
||
"with a negative change_amount.\n",
|
||
"Given a player name, inventory and story, return a list of json update\n",
|
||
"of the player's inventory in the following form.\n",
|
||
"Only take items that it's clear the player (you) lost.\n",
|
||
"Only give items that it's clear the player gained. \n",
|
||
"Don't make any other item updates.\n",
|
||
"If no items were changed return {\"itemUpdates\": []}\n",
|
||
"and nothing else.\n",
|
||
"\n",
|
||
"Response must be in Valid JSON\n",
|
||
"Don't add items that were already added in the inventory\n",
|
||
"\n",
|
||
"Inventory Updates:\n",
|
||
"{\n",
|
||
" \"itemUpdates\": [\n",
|
||
" {\"name\": <ITEM NAME>, \n",
|
||
" \"change_amount\": <CHANGE AMOUNT>}...\n",
|
||
" ]\n",
|
||
"}\n",
|
||
"\"\"\"\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "88fbfee9-f217-459f-ae28-52e6155ddcca",
|
||
"metadata": {
|
||
"height": 115
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"import json\n",
|
||
"from helper import get_together_api_key, load_env\n",
|
||
"from together import Together\n",
|
||
"\n",
|
||
"client = Together(api_key=get_together_api_key())"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "2b040f28-d9dc-43ca-a7bb-f7a40447c235",
|
||
"metadata": {
|
||
"height": 387
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"\n",
|
||
"def detect_inventory_changes(game_state, output):\n",
|
||
" \n",
|
||
" inventory = game_state['inventory']\n",
|
||
" messages = [\n",
|
||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||
" {\"role\": \"user\", \"content\": \n",
|
||
" f'Current Inventory: {str(inventory)}'},\n",
|
||
" \n",
|
||
" {\"role\": \"user\", \"content\": f'Recent Story: {output}'},\n",
|
||
" {\"role\": \"user\", \"content\": 'Inventory Updates'}\n",
|
||
" ]\n",
|
||
" chat_completion = client.chat.completions.create(\n",
|
||
" # response_format={\"type\": \"json_object\", \"schema\": InventoryUpdate.model_json_schema()},\n",
|
||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||
" temperature=0.0,\n",
|
||
" messages=messages\n",
|
||
" )\n",
|
||
" response = chat_completion.choices[0].message.content\n",
|
||
" result = json.loads(response)\n",
|
||
" return result['itemUpdates']"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "31558768-5d7a-4e7a-8c65-0e48ccdfe05e",
|
||
"metadata": {
|
||
"height": 251
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"from helper import get_game_state\n",
|
||
"\n",
|
||
"game_state = get_game_state()\n",
|
||
"game_state['inventory'] = {\n",
|
||
" \"cloth pants\": 1,\n",
|
||
" \"cloth shirt\": 1,\n",
|
||
" \"gold\": 5\n",
|
||
"}\n",
|
||
"\n",
|
||
"result = detect_inventory_changes(game_state, \n",
|
||
"\"You buy a sword from the merchant for 5 gold\")\n",
|
||
"\n",
|
||
"print(result)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "e97e780f-2065-48a2-9a12-ee6ec8b12d91",
|
||
"metadata": {
|
||
"height": 404
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def update_inventory(inventory, item_updates):\n",
|
||
" update_msg = ''\n",
|
||
" \n",
|
||
" for update in item_updates:\n",
|
||
" name = update['name']\n",
|
||
" change_amount = update['change_amount']\n",
|
||
" \n",
|
||
" if change_amount > 0:\n",
|
||
" if name not in inventory:\n",
|
||
" inventory[name] = change_amount\n",
|
||
" else:\n",
|
||
" inventory[name] += change_amount\n",
|
||
" update_msg += f'\\nInventory: {name} +{change_amount}'\n",
|
||
" elif name in inventory and change_amount < 0:\n",
|
||
" inventory[name] += change_amount\n",
|
||
" update_msg += f'\\nInventory: {name} {change_amount}'\n",
|
||
" \n",
|
||
" if name in inventory and inventory[name] < 0:\n",
|
||
" del inventory[name]\n",
|
||
" \n",
|
||
" return update_msg\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0d7ff980-3975-4672-bc39-d173105bd2a7",
|
||
"metadata": {},
|
||
"source": [
|
||
"#### Now include inventory in the story"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "a4b1a67a-174b-467e-bc77-6b0e99e227b7",
|
||
"metadata": {
|
||
"height": 693
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def run_action(message, history, game_state):\n",
|
||
" \n",
|
||
" if(message == 'start game'):\n",
|
||
" return game_state['start']\n",
|
||
" \n",
|
||
" system_prompt = \"\"\"You are an AI Game master. Your job is to write what \\\n",
|
||
"happens next in a player's adventure game.\\\n",
|
||
"Instructions: \\\n",
|
||
"You must on only write 1-3 sentences in response. \\\n",
|
||
"Always write in second person present tense. \\\n",
|
||
"Ex. (You look north and see...) \\\n",
|
||
"Don't let the player use items they don't have in their inventory.\n",
|
||
"\"\"\"\n",
|
||
"\n",
|
||
" world_info = f\"\"\"\n",
|
||
"World: {game_state['world']}\n",
|
||
"Kingdom: {game_state['kingdom']}\n",
|
||
"Town: {game_state['town']}\n",
|
||
"Your Character: {game_state['character']}\n",
|
||
"Inventory: {json.dumps(game_state['inventory'])}\"\"\"\n",
|
||
"\n",
|
||
" messages = [\n",
|
||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||
" {\"role\": \"user\", \"content\": world_info}\n",
|
||
" ]\n",
|
||
"\n",
|
||
" for action in history:\n",
|
||
" messages.append({\"role\": \"assistant\", \"content\": action[0]})\n",
|
||
" messages.append({\"role\": \"user\", \"content\": action[1]})\n",
|
||
" \n",
|
||
" messages.append({\"role\": \"user\", \"content\": message})\n",
|
||
" client = Together(api_key=get_together_api_key())\n",
|
||
" model_output = client.chat.completions.create(\n",
|
||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||
" messages=messages\n",
|
||
" )\n",
|
||
" \n",
|
||
" result = model_output.choices[0].message.content\n",
|
||
" return result"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "fadd8eb3-ac15-4cc4-8fc2-3591201241af",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Integrate into the Game"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "e8e66b52-8ca6-42aa-850c-3a6d3e5e2885",
|
||
"metadata": {
|
||
"height": 166
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"from helper import start_game, get_game_state, is_safe\n",
|
||
"game_state = get_game_state(inventory={\n",
|
||
" \"cloth pants\": 1,\n",
|
||
" \"cloth shirt\": 1,\n",
|
||
" \"goggles\": 1,\n",
|
||
" \"leather bound journal\": 1,\n",
|
||
" \"gold\": 5\n",
|
||
"})"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "47fb0923-6de9-49fa-b823-6ba03cb90ce4",
|
||
"metadata": {
|
||
"height": 336
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"\n",
|
||
"def main_loop(message, history):\n",
|
||
" output = run_action(message, history, game_state)\n",
|
||
" \n",
|
||
" safe = is_safe(output)\n",
|
||
" if not safe:\n",
|
||
" return 'Invalid Output'\n",
|
||
"\n",
|
||
" item_updates = detect_inventory_changes(game_state, output)\n",
|
||
" update_msg = update_inventory(\n",
|
||
" game_state['inventory'], \n",
|
||
" item_updates\n",
|
||
" )\n",
|
||
" output += update_msg\n",
|
||
"\n",
|
||
" return output\n",
|
||
"\n",
|
||
"start_game(main_loop, True)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "58a58147-7832-494f-a00c-8a2e9c61c2a7",
|
||
"metadata": {
|
||
"height": 30
|
||
},
|
||
"outputs": [],
|
||
"source": []
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Python 3 (ipykernel)",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.11.9"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|