Spaces:
Running
on
T4
Running
on
T4
daviddobas
commited on
Commit
·
c69a32f
1
Parent(s):
426a708
Improved prompt and logic
Browse files- src/game_engine.js +54 -41
- src/shyguy.js +52 -18
- src/shyguy_llm.js +8 -3
- src/story_engine.js +33 -12
src/game_engine.js
CHANGED
@@ -165,11 +165,11 @@ export class GameEngine {
|
|
165 |
// Debug controls
|
166 |
this.initDebugControls();
|
167 |
|
168 |
-
//
|
169 |
this.gridMapTypes = {
|
170 |
-
floor: 0,
|
171 |
-
wall: 1,
|
172 |
-
door: 2,
|
173 |
};
|
174 |
|
175 |
// load assets for drawing the scene
|
@@ -283,6 +283,9 @@ export class GameEngine {
|
|
283 |
document.addEventListener("keyup", this.handleKeyUp);
|
284 |
|
285 |
// Initialize with game view
|
|
|
|
|
|
|
286 |
|
287 |
this.sendButton.addEventListener("click", this.handleSendMessage);
|
288 |
this.dialogueContinueButton.addEventListener("click", this.handleDialogueContinue);
|
@@ -367,13 +370,13 @@ export class GameEngine {
|
|
367 |
for (let col = 0; col < this.gridCols; col++) {
|
368 |
// Set walls and obstacles (in future)
|
369 |
if (row === 0 || row === this.gridRows - 1 || col === 0 || col === this.gridCols - 1) {
|
370 |
-
this.backgroundGridMap[row][col] = this.gridMapTypes.wall;
|
371 |
} else {
|
372 |
-
this.backgroundGridMap[row][col] = this.gridMapTypes.floor;
|
373 |
}
|
374 |
}
|
375 |
}
|
376 |
-
this.backgroundGridMap[0][1] = this.gridMapTypes.door;
|
377 |
}
|
378 |
|
379 |
checkWallCollision(sprite, newX, newY) {
|
@@ -388,7 +391,9 @@ export class GameEngine {
|
|
388 |
for (let row = gridY; row <= Math.floor((y + sprite.height) / (sprite.height / 2)); row++) {
|
389 |
for (let col = gridX; col <= Math.floor((x + sprite.width) / (sprite.width * 1.33)); col++) {
|
390 |
if (row >= 0 && row < this.gridRows && col >= 0 && col < this.gridCols) {
|
391 |
-
|
|
|
|
|
392 |
return true;
|
393 |
}
|
394 |
}
|
@@ -567,7 +572,8 @@ export class GameEngine {
|
|
567 |
}
|
568 |
|
569 |
handleKeyDown(e) {
|
570 |
-
|
|
|
571 |
this.keys[e.key] = true;
|
572 |
this.wingmanSprite.moving = true;
|
573 |
} else if (e.key === "Enter" && this.currentView === "game" && !e.shiftKey) {
|
@@ -664,7 +670,7 @@ export class GameEngine {
|
|
664 |
if (isClose) {
|
665 |
// pause the game
|
666 |
target.enabled = false;
|
667 |
-
this.stopShyguyAnimation(
|
668 |
|
669 |
if (target.label === EXIT_LABEL) {
|
670 |
this.gameOver = true;
|
@@ -738,7 +744,7 @@ export class GameEngine {
|
|
738 |
this.showContinueButton();
|
739 |
}
|
740 |
|
741 |
-
stopShyguyAnimation(
|
742 |
this.shyguySprite.moving = false;
|
743 |
this.shyguySprite.frameX = 0;
|
744 |
this.shyguySprite.target = null;
|
@@ -753,52 +759,47 @@ export class GameEngine {
|
|
753 |
const x = col * this.wall.width;
|
754 |
const y = row * this.wall.height;
|
755 |
|
756 |
-
if (this.backgroundGridMap[row][col] === this.gridMapTypes.wall) {
|
757 |
this.ctx.drawImage(this.wall.image, x, y, this.wall.width, this.wall.height);
|
758 |
-
} else if (this.backgroundGridMap[row][col] === this.gridMapTypes.floor) {
|
759 |
this.ctx.drawImage(this.floor.image, x, y, this.floor.width, this.floor.height);
|
760 |
-
} else if (this.backgroundGridMap[row][col] === this.gridMapTypes.door) {
|
761 |
this.ctx.drawImage(this.door.image, x, y, this.door.width, this.door.height);
|
762 |
}
|
763 |
}
|
764 |
}
|
765 |
|
|
|
766 |
this.drawTargetSprite(this.jessicaSprite, this.targets.girl);
|
767 |
this.drawTargetSprite(this.barSprite, this.targets.bar);
|
768 |
this.drawTargetSprite(this.djSprite, this.targets.dj);
|
769 |
this.drawTargetSprite(this.sisterSprite, this.targets.sister);
|
770 |
|
771 |
// Draw shyguy
|
772 |
-
this.
|
773 |
-
this.shyguySprite.image,
|
774 |
-
this.shyguySprite.frameX * this.shyguySprite.width,
|
775 |
-
this.shyguySprite.frameY * this.shyguySprite.height,
|
776 |
-
this.shyguySprite.width,
|
777 |
-
this.shyguySprite.height,
|
778 |
-
this.shyguySprite.x,
|
779 |
-
this.shyguySprite.y,
|
780 |
-
this.shyguySprite.width,
|
781 |
-
this.shyguySprite.height
|
782 |
-
);
|
783 |
|
784 |
// Draw wingman
|
785 |
-
this.
|
786 |
-
this.wingmanSprite.image,
|
787 |
-
this.wingmanSprite.frameX * this.wingmanSprite.width,
|
788 |
-
this.wingmanSprite.frameY * this.wingmanSprite.height,
|
789 |
-
this.wingmanSprite.width,
|
790 |
-
this.wingmanSprite.height,
|
791 |
-
this.wingmanSprite.x,
|
792 |
-
this.wingmanSprite.y,
|
793 |
-
this.wingmanSprite.width,
|
794 |
-
this.wingmanSprite.height
|
795 |
-
);
|
796 |
}
|
797 |
|
798 |
drawTargetSprite(sprite, target) {
|
799 |
this.ctx.drawImage(sprite.image, target.x, target.y, target.width, target.height);
|
800 |
}
|
801 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
802 |
switchView(viewName) {
|
803 |
if (viewName === this.currentView) return;
|
804 |
|
@@ -923,7 +924,6 @@ export class GameEngine {
|
|
923 |
}
|
924 |
|
925 |
resolveAction(action) {
|
926 |
-
// TODO: resolve the action
|
927 |
switch (action) {
|
928 |
case "stay_idle":
|
929 |
this.setNewTarget(null);
|
@@ -970,6 +970,11 @@ export class GameEngine {
|
|
970 |
// TODO: save conversation history
|
971 |
await this.shyguy.learnFromWingman(message);
|
972 |
console.log("[ShyguyLLM]: Next action: ", action);
|
|
|
|
|
|
|
|
|
|
|
973 |
this.resolveAction(action);
|
974 |
});
|
975 |
}
|
@@ -993,6 +998,7 @@ export class GameEngine {
|
|
993 |
handlePlayAgain() {
|
994 |
this.clearChat(this.gameChatContainer);
|
995 |
this.resetGame();
|
|
|
996 |
this.switchView("game");
|
997 |
}
|
998 |
|
@@ -1075,9 +1081,12 @@ export class GameEngine {
|
|
1075 |
}
|
1076 |
|
1077 |
this.switchView("game");
|
1078 |
-
this.shyguyLLM.getShyGuyResponse("").then((response) => {
|
1079 |
const next_action = response.action;
|
1080 |
-
|
|
|
|
|
|
|
1081 |
this.resolveAction(next_action);
|
1082 |
});
|
1083 |
}
|
@@ -1117,14 +1126,18 @@ export class GameEngine {
|
|
1117 |
});
|
1118 |
}
|
1119 |
|
1120 |
-
|
1121 |
-
this.stopBackgroundMusic();
|
1122 |
this.gameOverMusic.pause();
|
1123 |
this.gameOverMusic.currentTime = 0;
|
1124 |
this.victoryMusic.pause();
|
1125 |
this.victoryMusic.currentTime = 0;
|
1126 |
}
|
1127 |
|
|
|
|
|
|
|
|
|
|
|
1128 |
lowerMusicVolume() {
|
1129 |
// Store original volumes if not already stored
|
1130 |
if (!this.originalVolumes) {
|
|
|
165 |
// Debug controls
|
166 |
this.initDebugControls();
|
167 |
|
168 |
+
// configure environment building blocks and enable passing them
|
169 |
this.gridMapTypes = {
|
170 |
+
floor: { index: 0, passable: true },
|
171 |
+
wall: { index: 1, passable: false },
|
172 |
+
door: { index: 2, passable: false },
|
173 |
};
|
174 |
|
175 |
// load assets for drawing the scene
|
|
|
283 |
document.addEventListener("keyup", this.handleKeyUp);
|
284 |
|
285 |
// Initialize with game view
|
286 |
+
const intialStatusText =
|
287 |
+
"You are playing as the Wingman. You can move around using arrow keys. Maybe Shyguy will listen to you or let you follow him around. Don't let him leave without the girl!";
|
288 |
+
this.updateStatus(intialStatusText);
|
289 |
|
290 |
this.sendButton.addEventListener("click", this.handleSendMessage);
|
291 |
this.dialogueContinueButton.addEventListener("click", this.handleDialogueContinue);
|
|
|
370 |
for (let col = 0; col < this.gridCols; col++) {
|
371 |
// Set walls and obstacles (in future)
|
372 |
if (row === 0 || row === this.gridRows - 1 || col === 0 || col === this.gridCols - 1) {
|
373 |
+
this.backgroundGridMap[row][col] = this.gridMapTypes.wall.index;
|
374 |
} else {
|
375 |
+
this.backgroundGridMap[row][col] = this.gridMapTypes.floor.index;
|
376 |
}
|
377 |
}
|
378 |
}
|
379 |
+
this.backgroundGridMap[0][1] = this.gridMapTypes.door.index;
|
380 |
}
|
381 |
|
382 |
checkWallCollision(sprite, newX, newY) {
|
|
|
391 |
for (let row = gridY; row <= Math.floor((y + sprite.height) / (sprite.height / 2)); row++) {
|
392 |
for (let col = gridX; col <= Math.floor((x + sprite.width) / (sprite.width * 1.33)); col++) {
|
393 |
if (row >= 0 && row < this.gridRows && col >= 0 && col < this.gridCols) {
|
394 |
+
const cellType = this.backgroundGridMap[row][col];
|
395 |
+
const typeInfo = Object.values(this.gridMapTypes).find((type) => type.index === cellType);
|
396 |
+
if (typeInfo && !typeInfo.passable) {
|
397 |
return true;
|
398 |
}
|
399 |
}
|
|
|
572 |
}
|
573 |
|
574 |
handleKeyDown(e) {
|
575 |
+
// Only handle arrow keys for player movement if the text input is not focused
|
576 |
+
if (e.key in this.keys && !document.activeElement.matches('input[type="text"], textarea')) {
|
577 |
this.keys[e.key] = true;
|
578 |
this.wingmanSprite.moving = true;
|
579 |
} else if (e.key === "Enter" && this.currentView === "game" && !e.shiftKey) {
|
|
|
670 |
if (isClose) {
|
671 |
// pause the game
|
672 |
target.enabled = false;
|
673 |
+
this.stopShyguyAnimation();
|
674 |
|
675 |
if (target.label === EXIT_LABEL) {
|
676 |
this.gameOver = true;
|
|
|
744 |
this.showContinueButton();
|
745 |
}
|
746 |
|
747 |
+
stopShyguyAnimation() {
|
748 |
this.shyguySprite.moving = false;
|
749 |
this.shyguySprite.frameX = 0;
|
750 |
this.shyguySprite.target = null;
|
|
|
759 |
const x = col * this.wall.width;
|
760 |
const y = row * this.wall.height;
|
761 |
|
762 |
+
if (this.backgroundGridMap[row][col] === this.gridMapTypes.wall.index) {
|
763 |
this.ctx.drawImage(this.wall.image, x, y, this.wall.width, this.wall.height);
|
764 |
+
} else if (this.backgroundGridMap[row][col] === this.gridMapTypes.floor.index) {
|
765 |
this.ctx.drawImage(this.floor.image, x, y, this.floor.width, this.floor.height);
|
766 |
+
} else if (this.backgroundGridMap[row][col] === this.gridMapTypes.door.index) {
|
767 |
this.ctx.drawImage(this.door.image, x, y, this.door.width, this.door.height);
|
768 |
}
|
769 |
}
|
770 |
}
|
771 |
|
772 |
+
// Draw npcs with targets
|
773 |
this.drawTargetSprite(this.jessicaSprite, this.targets.girl);
|
774 |
this.drawTargetSprite(this.barSprite, this.targets.bar);
|
775 |
this.drawTargetSprite(this.djSprite, this.targets.dj);
|
776 |
this.drawTargetSprite(this.sisterSprite, this.targets.sister);
|
777 |
|
778 |
// Draw shyguy
|
779 |
+
this.drawPlayerSprite(this.shyguySprite);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
780 |
|
781 |
// Draw wingman
|
782 |
+
this.drawPlayerSprite(this.wingmanSprite);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
783 |
}
|
784 |
|
785 |
drawTargetSprite(sprite, target) {
|
786 |
this.ctx.drawImage(sprite.image, target.x, target.y, target.width, target.height);
|
787 |
}
|
788 |
|
789 |
+
drawPlayerSprite(sprite) {
|
790 |
+
this.ctx.drawImage(
|
791 |
+
sprite.image,
|
792 |
+
sprite.frameX * sprite.width,
|
793 |
+
sprite.frameY * sprite.height,
|
794 |
+
sprite.width,
|
795 |
+
sprite.height,
|
796 |
+
sprite.x,
|
797 |
+
sprite.y,
|
798 |
+
sprite.width,
|
799 |
+
sprite.height
|
800 |
+
);
|
801 |
+
}
|
802 |
+
|
803 |
switchView(viewName) {
|
804 |
if (viewName === this.currentView) return;
|
805 |
|
|
|
924 |
}
|
925 |
|
926 |
resolveAction(action) {
|
|
|
927 |
switch (action) {
|
928 |
case "stay_idle":
|
929 |
this.setNewTarget(null);
|
|
|
970 |
// TODO: save conversation history
|
971 |
await this.shyguy.learnFromWingman(message);
|
972 |
console.log("[ShyguyLLM]: Next action: ", action);
|
973 |
+
this.shyguy.last_actions.push(action);
|
974 |
+
if (this.shyguy.num_beers >= 1) {
|
975 |
+
console.log("Updating status to: Shyguy is drunk. Try pushing him.");
|
976 |
+
this.updateStatus("Shyguy is drunk. Try pushing him.");
|
977 |
+
}
|
978 |
this.resolveAction(action);
|
979 |
});
|
980 |
}
|
|
|
998 |
handlePlayAgain() {
|
999 |
this.clearChat(this.gameChatContainer);
|
1000 |
this.resetGame();
|
1001 |
+
this.stopGameOverMusic();
|
1002 |
this.switchView("game");
|
1003 |
}
|
1004 |
|
|
|
1081 |
}
|
1082 |
|
1083 |
this.switchView("game");
|
1084 |
+
this.shyguyLLM.getShyGuyResponse("Where do you go next? Your available actions are: " + this.shyguy.getAvailableActions()).then((response) => {
|
1085 |
const next_action = response.action;
|
1086 |
+
if (this.shyguy.num_beers >= 1) {
|
1087 |
+
console.log("Updating status to: Shyguy is drunk. Try pushing him.");
|
1088 |
+
this.updateStatus("Shyguy is drunk. Try pushing him.");
|
1089 |
+
}
|
1090 |
this.resolveAction(next_action);
|
1091 |
});
|
1092 |
}
|
|
|
1126 |
});
|
1127 |
}
|
1128 |
|
1129 |
+
stopGameOverMusic() {
|
|
|
1130 |
this.gameOverMusic.pause();
|
1131 |
this.gameOverMusic.currentTime = 0;
|
1132 |
this.victoryMusic.pause();
|
1133 |
this.victoryMusic.currentTime = 0;
|
1134 |
}
|
1135 |
|
1136 |
+
stopAllMusic() {
|
1137 |
+
this.stopBackgroundMusic();
|
1138 |
+
this.stopGameOverMusic();
|
1139 |
+
}
|
1140 |
+
|
1141 |
lowerMusicVolume() {
|
1142 |
// Store original volumes if not already stored
|
1143 |
if (!this.originalVolumes) {
|
src/shyguy.js
CHANGED
@@ -4,27 +4,34 @@ export class Shyguy {
|
|
4 |
constructor() {
|
5 |
this.num_beers = 0;
|
6 |
this.courage = 1;
|
7 |
-
this.personality = "This is the Shyguy. He is shy and introverted. He is also a bit of a nerd. He fell in love with Jessica.
|
8 |
this.lessons_learned = "";
|
9 |
this.conversation_history = "";
|
10 |
this.song_playing = "Let it be";
|
11 |
this.imgpath = "assets/assets/shyguy_headshot.jpeg";
|
|
|
|
|
|
|
12 |
this.last_actions = [];
|
13 |
}
|
14 |
|
15 |
getSystemPrompt() {
|
16 |
if (this.num_beers >= 3) {
|
17 |
-
return `${this.personality}. His courage is ${this.courage} on the level 1 to 10. If his courage is higher than 5, he is self-confident. He had too many beers and he is drunk. He talks about how drunk he is. Follow the following lessons: ${this.lessons_learned}`;
|
18 |
} else if (this.num_beers == 2) {
|
19 |
-
return `This is Shyguy. He had two beers, so he feels relaxed and he can talk with anyone. Follow the following lessons: ${this.lessons_learned}
|
20 |
}
|
21 |
else {
|
22 |
-
return `${this.personality}. His courage is ${this.courage} on the level 1 to 10. If his courage is higher than 5, he is self-confident. He is really shy and he fears talking with people. It is not easy to persuade him. He
|
23 |
}
|
24 |
}
|
25 |
|
26 |
appendLesson(lesson) {
|
27 |
this.lessons_learned += lesson + "\n";
|
|
|
|
|
|
|
|
|
28 |
}
|
29 |
|
30 |
appendConversationHistory(conversation_history) {
|
@@ -32,35 +39,54 @@ export class Shyguy {
|
|
32 |
}
|
33 |
|
34 |
async learnLesson(entityName){
|
|
|
|
|
|
|
|
|
35 |
const summaryLLM = new LLM();
|
36 |
const summary = await summaryLLM.getChatCompletion(
|
37 |
-
`Summarize in one sentence what Shyguy should say when talking to ${entityName}. Do not confuse Jessica and Jessica's sister. If there is nothing relevant about what to say to Jessica, say Nothing relevant.`,
|
38 |
this.conversation_history
|
39 |
);
|
40 |
this.appendLesson(`When talking to ${entityName}, ${summary}`);
|
41 |
}
|
42 |
|
43 |
async learnFromWingman(wingman_message) {
|
44 |
-
const summaryLLM = new LLM();
|
45 |
console.log("Wingman message: ", wingman_message);
|
|
|
46 |
const summary = await summaryLLM.getChatCompletion(
|
47 |
-
`
|
48 |
wingman_message
|
49 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
this.appendLesson(summary);
|
51 |
}
|
52 |
|
53 |
getAvailableActions() {
|
54 |
let actions = {};
|
55 |
const lastAction = this.last_actions[this.last_actions.length - 1];
|
|
|
56 |
|
57 |
-
// When sober, can only go to the bar or home
|
58 |
if (this.num_beers === 0) {
|
59 |
actions = {
|
60 |
"go_bar": {
|
61 |
description: "Head to the bar.",
|
62 |
location: "bar",
|
63 |
},
|
|
|
|
|
|
|
|
|
64 |
"go_home": {
|
65 |
description: "Give up and head home",
|
66 |
location: "exit",
|
@@ -75,19 +101,26 @@ export class Shyguy {
|
|
75 |
// After 2+ beers, all actions except going home are available
|
76 |
actions = {
|
77 |
"go_bar": {
|
78 |
-
description: "
|
79 |
location: "bar",
|
80 |
},
|
81 |
"go_dj": {
|
82 |
-
description: "
|
83 |
location: "dj_booth",
|
84 |
},
|
85 |
"go_sister": {
|
86 |
-
description: "
|
87 |
location: "sister",
|
88 |
},
|
89 |
"go_girl": {
|
90 |
-
description: "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
location: "girl",
|
92 |
}
|
93 |
};
|
@@ -95,23 +128,23 @@ export class Shyguy {
|
|
95 |
// After 1 beer but less than 2, all actions are available
|
96 |
actions = {
|
97 |
"go_bar": {
|
98 |
-
description: "
|
99 |
location: "bar",
|
100 |
},
|
101 |
"go_home": {
|
102 |
-
description: "
|
103 |
location: "exit",
|
104 |
},
|
105 |
"go_dj": {
|
106 |
-
description: "
|
107 |
location: "dj_booth",
|
108 |
},
|
109 |
"go_sister": {
|
110 |
-
description: "
|
111 |
location: "sister",
|
112 |
},
|
113 |
"go_girl": {
|
114 |
-
description: "
|
115 |
location: "girl",
|
116 |
}
|
117 |
};
|
@@ -121,7 +154,8 @@ export class Shyguy {
|
|
121 |
if (lastAction && actions[lastAction]) {
|
122 |
delete actions[lastAction];
|
123 |
}
|
124 |
-
|
|
|
125 |
return actions;
|
126 |
}
|
127 |
}
|
|
|
4 |
constructor() {
|
5 |
this.num_beers = 0;
|
6 |
this.courage = 1;
|
7 |
+
this.personality = "This is the Shyguy. He is shy and introverted. He is also a bit of a nerd. He fell in love with Jessica. He never talked to Jessica before.To Jessica, he talks about algorithms. Jessica does not talk about algorithms. He is super shy.";
|
8 |
this.lessons_learned = "";
|
9 |
this.conversation_history = "";
|
10 |
this.song_playing = "Let it be";
|
11 |
this.imgpath = "assets/assets/shyguy_headshot.jpeg";
|
12 |
+
this.met_dj = false;
|
13 |
+
this.met_bar = false;
|
14 |
+
this.met_sister = false;
|
15 |
this.last_actions = [];
|
16 |
}
|
17 |
|
18 |
getSystemPrompt() {
|
19 |
if (this.num_beers >= 3) {
|
20 |
+
return `${this.personality}. His courage is ${this.courage} on the level 1 to 10. If his courage is higher than 5, he is self-confident. He had too many beers and he is drunk. He talks about how drunk he is. Follow the following lessons: ${this.lessons_learned}. ${this.met_dj ? "He has met the DJ already." : "He has not met the DJ yet."} ${this.met_bar ? "He has met the bartender already." : "He has not met the bartender yet."} ${this.met_sister ? "He has met Jessica's sister already." : "He has not met Jessica's sister yet."}`;
|
21 |
} else if (this.num_beers == 2) {
|
22 |
+
return `This is Shyguy. He had two beers, so he feels relaxed and he can talk with anyone. ${this.met_dj ? "He has met the DJ already." : "He has not met the DJ yet."} ${this.met_bar ? "He has met the bartender already." : "He has not met the bartender yet."} ${this.met_sister ? "He has met Jessica's sister already." : "He has not met Jessica's sister yet."} Follow the following lessons: ${this.lessons_learned}.`;
|
23 |
}
|
24 |
else {
|
25 |
+
return `${this.personality}. His courage is ${this.courage} on the level 1 to 10. If his courage is higher than 5, he is self-confident. He is really shy and he fears talking with people. It is not easy to persuade him. He does not want to drink at first. ${this.met_dj ? "He has met the DJ already." : "He has not met the DJ yet."} ${this.met_bar ? "He has met the bartender already." : "He has not met the bartender yet."} ${this.met_sister ? "He has met Jessica's sister already." : "He has not met Jessica's sister yet."} Follow the following lessons: ${this.lessons_learned}.`;
|
26 |
}
|
27 |
}
|
28 |
|
29 |
appendLesson(lesson) {
|
30 |
this.lessons_learned += lesson + "\n";
|
31 |
+
// Keep only last 200 characters of lessons learned if too long
|
32 |
+
if (this.lessons_learned.length > 400) {
|
33 |
+
this.lessons_learned = this.lessons_learned.slice(-400);
|
34 |
+
}
|
35 |
}
|
36 |
|
37 |
appendConversationHistory(conversation_history) {
|
|
|
39 |
}
|
40 |
|
41 |
async learnLesson(entityName){
|
42 |
+
console.log("Conversation history: ", this.conversation_history);
|
43 |
+
if (this.conversation_history === "") {
|
44 |
+
return;
|
45 |
+
}
|
46 |
const summaryLLM = new LLM();
|
47 |
const summary = await summaryLLM.getChatCompletion(
|
48 |
+
`Summarize in one sentence what Shyguy should say when talking to ${entityName}. Do not confuse Jessica and Jessica's sister. If there is nothing relevant about what to say to Jessica, say Nothing relevant. Do not hallucinate. Do not make up things.`,
|
49 |
this.conversation_history
|
50 |
);
|
51 |
this.appendLesson(`When talking to ${entityName}, ${summary}`);
|
52 |
}
|
53 |
|
54 |
async learnFromWingman(wingman_message) {
|
|
|
55 |
console.log("Wingman message: ", wingman_message);
|
56 |
+
const summaryLLM = new LLM();
|
57 |
const summary = await summaryLLM.getChatCompletion(
|
58 |
+
`Summarize in one sentence what is learned from the message. Summary is one sentence. For example, if the wingman says "Let's have a beer", the output should be "Shyguy wants a beer". If the wingman says "Let's have vodka", the output should be "Shyguy wants vodka". If wingman just says "Hi", the output should be "Wingman said hi". Do not hallucinate. Do not make up things.`,
|
59 |
wingman_message
|
60 |
);
|
61 |
+
console.log("Summary learned from wingman: ", summary);
|
62 |
+
this.appendLesson(summary);
|
63 |
+
}
|
64 |
+
|
65 |
+
async learnFromConversation(conversation) {
|
66 |
+
console.log("Conversation: ", conversation);
|
67 |
+
const summaryLLM = new LLM();
|
68 |
+
const summary = await summaryLLM.getChatCompletion(
|
69 |
+
`Summarize in one sentence what happened in the conversation. Do not hallucinate. Do not make up things.`,
|
70 |
+
conversation
|
71 |
+
);
|
72 |
this.appendLesson(summary);
|
73 |
}
|
74 |
|
75 |
getAvailableActions() {
|
76 |
let actions = {};
|
77 |
const lastAction = this.last_actions[this.last_actions.length - 1];
|
78 |
+
console.log("[ShyGuy]: Last action: ", lastAction);
|
79 |
|
|
|
80 |
if (this.num_beers === 0) {
|
81 |
actions = {
|
82 |
"go_bar": {
|
83 |
description: "Head to the bar.",
|
84 |
location: "bar",
|
85 |
},
|
86 |
+
"go_dj": {
|
87 |
+
description: "Go to the DJ",
|
88 |
+
location: "dj_booth",
|
89 |
+
},
|
90 |
"go_home": {
|
91 |
description: "Give up and head home",
|
92 |
location: "exit",
|
|
|
101 |
// After 2+ beers, all actions except going home are available
|
102 |
actions = {
|
103 |
"go_bar": {
|
104 |
+
description: "Go to the bar",
|
105 |
location: "bar",
|
106 |
},
|
107 |
"go_dj": {
|
108 |
+
description: "GO to the DJ",
|
109 |
location: "dj_booth",
|
110 |
},
|
111 |
"go_sister": {
|
112 |
+
description: "Go to Jessica's sister",
|
113 |
location: "sister",
|
114 |
},
|
115 |
"go_girl": {
|
116 |
+
description: "Go to Jessica",
|
117 |
+
location: "girl",
|
118 |
+
}
|
119 |
+
};
|
120 |
+
} else if (this.num_beers >= 4) {
|
121 |
+
actions = {
|
122 |
+
"go_girl": {
|
123 |
+
description: "Go to Jessica",
|
124 |
location: "girl",
|
125 |
}
|
126 |
};
|
|
|
128 |
// After 1 beer but less than 2, all actions are available
|
129 |
actions = {
|
130 |
"go_bar": {
|
131 |
+
description: "Go to the bar",
|
132 |
location: "bar",
|
133 |
},
|
134 |
"go_home": {
|
135 |
+
description: "Go home",
|
136 |
location: "exit",
|
137 |
},
|
138 |
"go_dj": {
|
139 |
+
description: "Go to the DJ",
|
140 |
location: "dj_booth",
|
141 |
},
|
142 |
"go_sister": {
|
143 |
+
description: "Go to Jessica's sister",
|
144 |
location: "sister",
|
145 |
},
|
146 |
"go_girl": {
|
147 |
+
description: "Go to Jessica",
|
148 |
location: "girl",
|
149 |
}
|
150 |
};
|
|
|
154 |
if (lastAction && actions[lastAction]) {
|
155 |
delete actions[lastAction];
|
156 |
}
|
157 |
+
console.log("[ShyGuy]: Last actions: ", this.last_actions);
|
158 |
+
console.log("[ShyGuy]: Available actions: ", actions);
|
159 |
return actions;
|
160 |
}
|
161 |
}
|
src/shyguy_llm.js
CHANGED
@@ -6,9 +6,10 @@ export class ShyGuyLLM {
|
|
6 |
this.currentConversation = []; // Array to store current conversation messages
|
7 |
const availableActions = this.shyguy.getAvailableActions();
|
8 |
this.baseSystemPrompt = `You are ShyGuy, a socially awkward character at a homecoming party. You are shy.
|
9 |
-
The wingman progressively makes you more confident, and if you drink alcohol.
|
10 |
You know the wingman, he is your friend. You are talking to him.
|
11 |
You are willing to have a little alcohol.
|
|
|
12 |
The action should correspond to the dialogue.
|
13 |
Your responses should be in JSON format with two fields:
|
14 |
enum action {
|
@@ -37,7 +38,7 @@ export class ShyGuyLLM {
|
|
37 |
addToPrompt += `You are drunk and you start talking about how bad you feel. You are annoying. You talk about algorithms and then how bad you feel.\n`;
|
38 |
}
|
39 |
if (this.shyguy.courage < 2) {
|
40 |
-
addToPrompt += `You are shy. However, you believe wingman.`;
|
41 |
}
|
42 |
if (this.shyguy.courage >= 2) {
|
43 |
addToPrompt += `You talk normally.`;
|
@@ -76,12 +77,16 @@ export class ShyGuyLLM {
|
|
76 |
const actionsPrompt = `\nYour currently available actions are: ${Object.keys(availableActions)
|
77 |
.map((action) => `\n- ${action}: ${availableActions[action].description}`)
|
78 |
.join("")}`;
|
79 |
-
|
80 |
// Add the situation to current conversation
|
81 |
this.addToCurrentConversation('wingman', player_message);
|
82 |
|
|
|
|
|
|
|
83 |
const fullPrompt = this.getSystemPrompt() + actionsPrompt;
|
|
|
84 |
const response = await this.llm.getJsonCompletion(fullPrompt, player_message);
|
|
|
85 |
|
86 |
// Add ShyGuy's response to current conversation
|
87 |
this.addToCurrentConversation('shyguy', response.dialogue);
|
|
|
6 |
this.currentConversation = []; // Array to store current conversation messages
|
7 |
const availableActions = this.shyguy.getAvailableActions();
|
8 |
this.baseSystemPrompt = `You are ShyGuy, a socially awkward character at a homecoming party. You are shy.
|
9 |
+
The wingman progressively makes you more confident, and if you drink alcohol, you also get more confident.
|
10 |
You know the wingman, he is your friend. You are talking to him.
|
11 |
You are willing to have a little alcohol.
|
12 |
+
If there is no clear action, you go home. Do not stay idle twice in a row.
|
13 |
The action should correspond to the dialogue.
|
14 |
Your responses should be in JSON format with two fields:
|
15 |
enum action {
|
|
|
38 |
addToPrompt += `You are drunk and you start talking about how bad you feel. You are annoying. You talk about algorithms and then how bad you feel.\n`;
|
39 |
}
|
40 |
if (this.shyguy.courage < 2) {
|
41 |
+
addToPrompt += `You are shy. However, you believe wingman. You are willing to go the DJ, you are just shy.`;
|
42 |
}
|
43 |
if (this.shyguy.courage >= 2) {
|
44 |
addToPrompt += `You talk normally.`;
|
|
|
77 |
const actionsPrompt = `\nYour currently available actions are: ${Object.keys(availableActions)
|
78 |
.map((action) => `\n- ${action}: ${availableActions[action].description}`)
|
79 |
.join("")}`;
|
|
|
80 |
// Add the situation to current conversation
|
81 |
this.addToCurrentConversation('wingman', player_message);
|
82 |
|
83 |
+
// Add the conversation to shyguy's history
|
84 |
+
this.shyguy.conversation_history += `\nWingman: ${player_message}\n`;
|
85 |
+
|
86 |
const fullPrompt = this.getSystemPrompt() + actionsPrompt;
|
87 |
+
console.log("[ShyGuy]: Full prompt: ", fullPrompt);
|
88 |
const response = await this.llm.getJsonCompletion(fullPrompt, player_message);
|
89 |
+
console.log("[ShyGuy]: Response: ", response);
|
90 |
|
91 |
// Add ShyGuy's response to current conversation
|
92 |
this.addToCurrentConversation('shyguy', response.dialogue);
|
src/story_engine.js
CHANGED
@@ -19,13 +19,15 @@ export function nameToLabel(name) {
|
|
19 |
}
|
20 |
|
21 |
class Bar {
|
22 |
-
constructor() {
|
23 |
this.name = "bartender";
|
24 |
-
this.situation_prompt = "This conversation
|
25 |
-
this.personality = "This is the bartender. He always offers triple vodka in his first answer. Then he is helpful, but he says that he has a crush on Jessica's sister";
|
26 |
// this.past_conversation = "";
|
27 |
this.imgpath = "assets/assets/barman.jpeg";
|
28 |
this.output_format_prompt = "";
|
|
|
|
|
29 |
this.functionDescriptions = [
|
30 |
{
|
31 |
key: "analyzeConsumption",
|
@@ -34,11 +36,11 @@ class Bar {
|
|
34 |
parameters: {
|
35 |
num_beers: {
|
36 |
type: "number",
|
37 |
-
description: "Total number of beers
|
38 |
},
|
39 |
heavy_alcohol: {
|
40 |
type: "boolean",
|
41 |
-
description: "If heavy alcohol
|
42 |
},
|
43 |
},
|
44 |
},
|
@@ -47,7 +49,7 @@ class Bar {
|
|
47 |
}
|
48 |
|
49 |
getSystemPrompt() {
|
50 |
-
return `${this.personality}.`;
|
51 |
}
|
52 |
}
|
53 |
|
@@ -56,9 +58,10 @@ class DJ {
|
|
56 |
this.name = "DJ";
|
57 |
this.situation_prompt = "This is a conversation with the DJ.";
|
58 |
this.personality =
|
59 |
-
"This is the DJ. He is funny. He offers drugs to the shyguy. He can only play songs from the options [Let it be, Call me maybe, Shape of you]. If asked to play a song, he will play it and not propose any other song.";
|
60 |
// this.past_conversation = "";
|
61 |
this.imgpath = "assets/assets/dj.jpeg";
|
|
|
62 |
this.output_format_prompt = "";
|
63 |
this.functionDescriptions = [
|
64 |
{
|
@@ -77,7 +80,7 @@ class DJ {
|
|
77 |
}
|
78 |
|
79 |
getSystemPrompt() {
|
80 |
-
return `${this.personality}`;
|
81 |
}
|
82 |
}
|
83 |
|
@@ -91,6 +94,7 @@ class Sister {
|
|
91 |
"This is the sister of Jessica. She is a deeply religious Christian. Her first answer is rude. Her second answer is about christianity. First answer is helpful. If asked about the favourite song of the girl, she says happily that it is 'Call me maybe'.";
|
92 |
// this.past_conversation = "";
|
93 |
this.imgpath = "assets/assets/sister.jpeg";
|
|
|
94 |
this.functionDescriptions = [
|
95 |
{
|
96 |
key: "analyzeMood",
|
@@ -112,7 +116,7 @@ class Sister {
|
|
112 |
}
|
113 |
|
114 |
getSystemPrompt() {
|
115 |
-
return `${this.personality}. Her mood is ${this.mood} on the level 1 to 10. If the mood is low, she will be rude. If the mood is high, she will be helpful
|
116 |
}
|
117 |
}
|
118 |
|
@@ -124,6 +128,7 @@ class Girl {
|
|
124 |
this.imgpath = "assets/assets/jessica.jpeg";
|
125 |
this.output_format_prompt = "";
|
126 |
this.shyguy = shyguy;
|
|
|
127 |
this.functionDescriptions = [
|
128 |
{
|
129 |
key: "analyzeLiking",
|
@@ -141,9 +146,9 @@ class Girl {
|
|
141 |
|
142 |
getSystemPrompt() {
|
143 |
if (this.shyguy.song_playing === "Call me maybe") {
|
144 |
-
return `${this.personality}. She is very happy with the song playing. The first thing she says is that she really likes the music. Therefore she is nice and she likes shyguy. However, if he talks about algorithms, she does not like it and she becomes mean. Also if he talks with a lot of hesitation, she does not like it and she becomes mean
|
145 |
} else {
|
146 |
-
return `${this.personality}. She does not like the song that
|
147 |
}
|
148 |
}
|
149 |
}
|
@@ -186,7 +191,7 @@ export class StoryEngine {
|
|
186 |
// Initialize story engine properties with provided shyguy instance
|
187 |
this.shyguy = shyguy;
|
188 |
this.sister = new Sister();
|
189 |
-
this.bar = new Bar();
|
190 |
this.dj = new DJ();
|
191 |
this.girl = new Girl(shyguy);
|
192 |
}
|
@@ -224,6 +229,12 @@ export class StoryEngine {
|
|
224 |
|
225 |
// Append the conversation to shyguy's history
|
226 |
this.shyguy.conversation_history += `\nConversation with ${targetEntity.name}:\n${conversation}\n`;
|
|
|
|
|
|
|
|
|
|
|
|
|
227 |
|
228 |
let gameOver = this.decideGameOver(conversation_output.analysis.parameters.game_over);
|
229 |
let gameSuccesful = this.decideGameSuccesful(conversation_output.analysis.parameters.likes_shyguy);
|
@@ -237,6 +248,16 @@ export class StoryEngine {
|
|
237 |
this.updateStates(conversation_output.analysis, targetEntity.name);
|
238 |
console.log("shyguy num_beers: " + this.shyguy.num_beers);
|
239 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
240 |
return {
|
241 |
conversation: conversation,
|
242 |
char1imgpath: this.shyguy.imgpath,
|
|
|
19 |
}
|
20 |
|
21 |
class Bar {
|
22 |
+
constructor(shyguy) {
|
23 |
this.name = "bartender";
|
24 |
+
this.situation_prompt = "This is a conversation with the bartender.";
|
25 |
+
this.personality = "This is the bartender. He always offers triple vodka in his first answer. Then he is helpful, but he says that he has a crush on Jessica's sister.";
|
26 |
// this.past_conversation = "";
|
27 |
this.imgpath = "assets/assets/barman.jpeg";
|
28 |
this.output_format_prompt = "";
|
29 |
+
this.met_shyguy = false;
|
30 |
+
this.shyguy = shyguy;
|
31 |
this.functionDescriptions = [
|
32 |
{
|
33 |
key: "analyzeConsumption",
|
|
|
36 |
parameters: {
|
37 |
num_beers: {
|
38 |
type: "number",
|
39 |
+
description: "Total number of beers ordered by Shyguy for himself in the conversation. ",
|
40 |
},
|
41 |
heavy_alcohol: {
|
42 |
type: "boolean",
|
43 |
+
description: "If Shyguy ordered or agreed to have heavy alcohol (like vodka), True",
|
44 |
},
|
45 |
},
|
46 |
},
|
|
|
49 |
}
|
50 |
|
51 |
getSystemPrompt() {
|
52 |
+
return `${this.personality}. ${this.met_shyguy ? "He has met Shyguy already." : "He has not met Shyguy yet."}. The song playing is ${this.shyguy.song_playing}.`;
|
53 |
}
|
54 |
}
|
55 |
|
|
|
58 |
this.name = "DJ";
|
59 |
this.situation_prompt = "This is a conversation with the DJ.";
|
60 |
this.personality =
|
61 |
+
"This is the DJ. He is funny. He offers drugs to the shyguy. He can only play songs from the options [Let it be, Call me maybe, Shape of you]. If asked to play a song, he will play it and not propose any other song. He answers in two sentences maximum. He is funny.";
|
62 |
// this.past_conversation = "";
|
63 |
this.imgpath = "assets/assets/dj.jpeg";
|
64 |
+
this.met_shyguy = false;
|
65 |
this.output_format_prompt = "";
|
66 |
this.functionDescriptions = [
|
67 |
{
|
|
|
80 |
}
|
81 |
|
82 |
getSystemPrompt() {
|
83 |
+
return `${this.personality}. ${this.met_shyguy ? "He has met Shyguy already." : "He has not met Shyguy yet."}`;
|
84 |
}
|
85 |
}
|
86 |
|
|
|
94 |
"This is the sister of Jessica. She is a deeply religious Christian. Her first answer is rude. Her second answer is about christianity. First answer is helpful. If asked about the favourite song of the girl, she says happily that it is 'Call me maybe'.";
|
95 |
// this.past_conversation = "";
|
96 |
this.imgpath = "assets/assets/sister.jpeg";
|
97 |
+
this.met_shyguy = false;
|
98 |
this.functionDescriptions = [
|
99 |
{
|
100 |
key: "analyzeMood",
|
|
|
116 |
}
|
117 |
|
118 |
getSystemPrompt() {
|
119 |
+
return `${this.personality}. Her mood is ${this.mood} on the level 1 to 10. If the mood is low, she will be rude. If the mood is high, she will be helpful. ${this.met_shyguy ? "She has met Shyguy already." : "She has not met Shyguy yet."}`;
|
120 |
}
|
121 |
}
|
122 |
|
|
|
128 |
this.imgpath = "assets/assets/jessica.jpeg";
|
129 |
this.output_format_prompt = "";
|
130 |
this.shyguy = shyguy;
|
131 |
+
this.met_shyguy = false;
|
132 |
this.functionDescriptions = [
|
133 |
{
|
134 |
key: "analyzeLiking",
|
|
|
146 |
|
147 |
getSystemPrompt() {
|
148 |
if (this.shyguy.song_playing === "Call me maybe") {
|
149 |
+
return `${this.personality}. She is very happy with the song playing. The first thing she says is that she really likes the music. Therefore she is nice and she likes shyguy. However, if he talks about algorithms, she does not like it and she becomes mean. Also if he talks with a lot of hesitation, she does not like it and she becomes mean. ${this.met_shyguy ? "She has met Shyguy already." : "She has not met Shyguy yet."}`;
|
150 |
} else {
|
151 |
+
return `${this.personality}. She does not like the song that the DJ plays. The first thing she says is that the song is terrible. Then she is mean all the time. ${this.met_shyguy ? "She has met Shyguy already." : "She has not met Shyguy yet."}`;
|
152 |
}
|
153 |
}
|
154 |
}
|
|
|
191 |
// Initialize story engine properties with provided shyguy instance
|
192 |
this.shyguy = shyguy;
|
193 |
this.sister = new Sister();
|
194 |
+
this.bar = new Bar(shyguy);
|
195 |
this.dj = new DJ();
|
196 |
this.girl = new Girl(shyguy);
|
197 |
}
|
|
|
229 |
|
230 |
// Append the conversation to shyguy's history
|
231 |
this.shyguy.conversation_history += `\nConversation with ${targetEntity.name}:\n${conversation}\n`;
|
232 |
+
console.log(conversation);
|
233 |
+
const conversationString = Object.values(conversation)
|
234 |
+
.map(msg => `${msg.role}: ${msg.content}`)
|
235 |
+
.join('\n');
|
236 |
+
this.shyguy.learnFromConversation(conversationString);
|
237 |
+
console.log("Lessons learned from conversation with " + targetEntity.name + this.shyguy.lessons_learned);
|
238 |
|
239 |
let gameOver = this.decideGameOver(conversation_output.analysis.parameters.game_over);
|
240 |
let gameSuccesful = this.decideGameSuccesful(conversation_output.analysis.parameters.likes_shyguy);
|
|
|
248 |
this.updateStates(conversation_output.analysis, targetEntity.name);
|
249 |
console.log("shyguy num_beers: " + this.shyguy.num_beers);
|
250 |
|
251 |
+
targetEntity.met_shyguy = true;
|
252 |
+
|
253 |
+
if (targetEntity.name === "DJ") {
|
254 |
+
this.shyguy.met_dj = true;
|
255 |
+
} else if (targetEntity.name === "bartender") {
|
256 |
+
this.shyguy.met_bar = true;
|
257 |
+
} else if (targetEntity.name === "Jessica's sister") {
|
258 |
+
this.shyguy.met_sister = true;
|
259 |
+
}
|
260 |
+
|
261 |
return {
|
262 |
conversation: conversation,
|
263 |
char1imgpath: this.shyguy.imgpath,
|