Skip to content

Commit 3a6ace9

Browse files
authored
Merge pull request #68 from Visual-Intelligence-UMN/final-touch
added recorder
2 parents ba6c548 + 4016ed8 commit 3a6ace9

File tree

9 files changed

+117
-59
lines changed

9 files changed

+117
-59
lines changed

src/game/scenes/level1.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import { createScoreUI, resetScoreUI } from '../../langgraph/workflowUtils';
2828

2929
// import { createGenerateVisualizationButton } from '../../langgraph/visualizationGenerate';
3030

31-
import { saveHistory, createHistoryButton, createSimpleInstructionHUD, createDifficultySelector, addPDFIcon, pickAgentForSingleStrict, addTitleWithHoverInfo } from './levelHelper';
31+
import { saveHistory, createHistoryButton, createSimpleInstructionHUD, createDifficultySelector, addPDFIcon, pickAgentForSingleStrict, addTitleWithHoverInfo, createDownloadButton } from './levelHelper';
32+
import { recorder } from '../utils/recorder';
3233

3334

3435
const level = "level1"
@@ -633,6 +634,7 @@ export class Level1 extends ParentScene {
633634
this.cameras.main.setZoom(zoom);
634635
this.cameras.main.centerOn(mapWidth / 2, mapHeight / 2);
635636

637+
createDownloadButton(this, "level1");
636638
createHistoryButton(this, "level1");
637639

638640
// this.events.on('level-complete', () => {
@@ -649,6 +651,10 @@ export class Level1 extends ParentScene {
649651
this.showTryAgainMessage(score); // 下面第3步新增的小函数
650652
}
651653
});
654+
655+
// start recording
656+
recorder.startRecord();
657+
652658
}
653659

654660
private async choosePattern(pattern: string) {
@@ -790,6 +796,7 @@ return result;
790796
this.baseBallBtn.setDepth(1010);
791797

792798
}
799+
recorder.recordEvent('dataset_switched');
793800
});
794801
// console.log("ready to attach info icon for baseball");
795802
this.attachInfoIcon(this.baseBallBtn, 'baseball_groundtruth');
@@ -885,12 +892,15 @@ return result;
885892
this.kidneyBtn.setDepth(1010);
886893

887894
}
895+
recorder.recordEvent('dataset_switched');
888896
});
889897
// console.log("ready to attach info icon for kidney");
890898

891899
this.attachInfoIcon(this.kidneyBtn, 'kidney_groundtruth');
892900

893901
this.debateStartBtn.on('pointerdown', async () => {
902+
903+
recorder.recordEvent('simulation_started');
894904

895905
// Reset old UIs(ReportUI and ScoresUI)
896906
resetReportIcons(this);
@@ -1312,6 +1322,9 @@ return result;
13121322
});
13131323

13141324
nextLevelBtn.on('pointerdown', () => {
1325+
recorder.recordEvent('next_level_clicked');
1326+
1327+
13151328
this.scene.start('level2');
13161329
});
13171330
}

src/game/scenes/level2.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import { createScoreUI, resetScoreUI } from '../../langgraph/workflowUtils';
2828

2929
// import { createGenerateVisualizationButton } from '../../langgraph/visualizationGenerate';
3030

31-
import { saveHistory, createHistoryButton, createSimpleInstructionHUD, createDifficultySelector, addPDFIcon, pickAgentForSingleStrict, addTitleWithHoverInfo} from './levelHelper';
31+
import { saveHistory, createHistoryButton, createSimpleInstructionHUD, createDifficultySelector, addPDFIcon, pickAgentForSingleStrict, addTitleWithHoverInfo, createDownloadButton} from './levelHelper';
32+
import { recorder } from '../utils/recorder';
3233

3334

3435
const level = "level2";
@@ -651,6 +652,7 @@ export class Level2 extends ParentScene {
651652
}
652653
});
653654

655+
createDownloadButton(this, "level2");
654656
createHistoryButton(this, "level2");
655657
}
656658

@@ -768,6 +770,7 @@ export class Level2 extends ParentScene {
768770
}
769771
})
770772
.on("pointerdown", ()=>{
773+
recorder.recordEvent('dataset_switched');
771774
if(this.selectedDataset !== 'baseball'){
772775
this.selectedDataset = "baseball";
773776
this.selectedText?.destroy();
@@ -863,6 +866,7 @@ export class Level2 extends ParentScene {
863866
}
864867
})
865868
.on("pointerdown", ()=>{
869+
recorder.recordEvent('dataset_switched');
866870
if(this.selectedDataset !== 'kidney'){
867871
this.selectedDataset = "kidney";
868872
this.selectedText?.destroy();
@@ -894,6 +898,8 @@ export class Level2 extends ParentScene {
894898
this.attachInfoIcon(this.kidneyBtn, 'kidney_groundtruth');
895899

896900
this.debateStartBtn.on('pointerdown', async () => {
901+
902+
recorder.recordEvent('simulation_started');
897903

898904
// Reset old UIs(ReportUI and ScoresUI)
899905
resetReportIcons(this);
@@ -1314,6 +1320,7 @@ private createNextLevelButton() {
13141320
});
13151321

13161322
nextLevelBtn.on('pointerdown', () => {
1323+
recorder.recordEvent('next_level_clicked');
13171324
this.scene.start('level3');
13181325
});
13191326
}

src/game/scenes/level3.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import { createScoreUI, resetScoreUI } from '../../langgraph/workflowUtils';
2828

2929
// import { createGenerateVisualizationButton } from '../../langgraph/visualizationGenerate';
3030

31-
import { saveHistory, createHistoryButton, createSimpleInstructionHUD, createDifficultySelector, addPDFIcon, pickAgentForSingleStrict, addTitleWithHoverInfo} from './levelHelper';
31+
import { saveHistory, createHistoryButton, createSimpleInstructionHUD, createDifficultySelector, addPDFIcon, pickAgentForSingleStrict, addTitleWithHoverInfo, createDownloadButton} from './levelHelper';
32+
import { recorder } from '../utils/recorder';
3233

3334
const level = "level3"
3435

@@ -668,6 +669,7 @@ export class Level3 extends ParentScene {
668669
this.cameras.main.setZoom(zoom);
669670
this.cameras.main.centerOn(mapWidth / 2, mapHeight / 2);
670671

672+
createDownloadButton(this, "level2");
671673
createHistoryButton(this, "level2");
672674

673675
this.events.on('level-complete', (payload?: { score: number }) => {
@@ -796,6 +798,7 @@ return result;
796798
}
797799
})
798800
.on("pointerdown", ()=>{
801+
recorder.recordEvent('dataset_switched');
799802
if(this.selectedDataset !== 'baseball'){
800803
this.selectedDataset = "baseball";
801804
this.selectedText?.destroy();
@@ -891,6 +894,7 @@ return result;
891894
}
892895
})
893896
.on("pointerdown", ()=>{
897+
recorder.recordEvent('dataset_switched');
894898
if(this.selectedDataset !== 'kidney'){
895899
this.selectedDataset = "kidney";
896900
this.selectedText?.destroy();
@@ -922,6 +926,8 @@ return result;
922926
this.attachInfoIcon(this.kidneyBtn, 'kidney_groundtruth');
923927

924928
this.debateStartBtn.on('pointerdown', async () => {
929+
930+
recorder.recordEvent('simulation_started');
925931

926932
// Reset old UIs(ReportUI and ScoresUI)
927933
resetReportIcons(this);
@@ -1331,6 +1337,9 @@ return result;
13311337
});
13321338

13331339
nextLevelBtn.on('pointerdown', () => {
1340+
recorder.recordEvent('next_level_clicked');
1341+
recorder.endRecord();
1342+
13341343
this.scene.start('level1');
13351344
});
13361345
}

src/game/scenes/levelHelper.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22
import Phaser from 'phaser';
33
import { key } from '../constants';
44
import { Agent } from '../sprites/Agent';
5+
import { recorder } from '../utils/recorder';
6+
7+
export function createDownloadButton(scene: Phaser.Scene, level: string) {
8+
const screenWidth = scene.cameras.main.width;
9+
const screenHeight = scene.cameras.main.height;
10+
11+
const button = scene.add.text(screenWidth - 65, screenHeight - 80, '📊 Data Download', {
12+
fontSize: '18px',
13+
fontFamily: 'Verdana',
14+
color: '#ffffff',
15+
backgroundColor: '#000000',
16+
padding: { x: 10, y: 5 },
17+
})
18+
.setOrigin(0.5)
19+
.setScrollFactor(0)
20+
.setDepth(2000)
21+
.setInteractive();
22+
23+
button.on('pointerdown', () => {
24+
recorder.endRecord();
25+
});
26+
}
27+
528

629
// === History ===
730
interface HistoryRecord {
@@ -48,6 +71,8 @@ export function createHistoryButton(scene: Phaser.Scene, level: string) {
4871
});
4972
}
5073

74+
75+
5176
// Display history
5277
function showHistory(scene: Phaser.Scene, level: string) {
5378
const key = HISTORY_KEY_PREFIX + level;

src/game/sprites/Agent.ts

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Phaser from 'phaser';
33
import { key } from '../constants';
44
import { Inventory } from './Player';
55
import { EventBus } from '../EventBus';
6+
import { recorder } from '../utils/recorder';
67

78
enum Animation {
89
Left = 'player_left',
@@ -45,49 +46,6 @@ export class Agent extends Phaser.Physics.Arcade.Sprite {
4546
public static currentBiasedAgent: Agent | null = null; // Reservation: pointing to the "last selected"
4647

4748

48-
private agentInformation:string = "aaaaaaa";
49-
50-
public addMssgSprite(scene: Phaser.Scene, texture: string, frame?: string | number) {
51-
if (this.mssgSprite) {
52-
console.log("Updating message sprite for agent:", this.name);
53-
this.mssgSprite.setTexture(texture, frame);
54-
55-
this.mssgSprite.removeAllListeners();
56-
this.mssgSprite.disableInteractive();
57-
58-
if (texture === "agent_mssg") {
59-
this.mssgSprite.setInteractive({ useHandCursor: true });
60-
this.mssgSprite.on('pointerdown', () => {
61-
console.log(`Message sprite of ${this.name} clicked!`);
62-
this.changeNameTagColor('#00ff00');
63-
EventBus.emit("open-agent-information", {
64-
agent: this.name
65-
});
66-
});
67-
}
68-
return;
69-
}
70-
71-
console.log("Adding message sprite to agent:", this.name);
72-
this.mssgSprite = scene.add.image(this.x, this.y, texture, frame)
73-
.setOrigin(0.5, 1)
74-
.setDepth(10);
75-
76-
if (texture === "agent_mssg") {
77-
this.mssgSprite.setInteractive({ useHandCursor: true });
78-
this.mssgSprite.on('pointerdown', () => {
79-
console.log(`Message sprite of ${this.name} clicked!`);
80-
this.changeNameTagColor('#00ff00');
81-
EventBus.emit("open-agent-information", {
82-
agent: this.name
83-
});
84-
});
85-
}
86-
}
87-
public static biasedAgentsCount: number = 0; // Calculate the current number of biased agents in this level
88-
89-
90-
9149
private agentInformation:string = "aaaaaaa";
9250

9351
public addMssgSprite(scene: Phaser.Scene, texture: string, frame?: string | number) {
@@ -159,19 +117,6 @@ export class Agent extends Phaser.Physics.Arcade.Sprite {
159117
}
160118

161119

162-
public getAgentInformation(){
163-
return this.agentInformation;
164-
}
165-
166-
public setAgentInformation(info: string) {
167-
this.agentInformation = info;
168-
EventBus.emit("agent-information", {
169-
agent: this.name,
170-
mssg: this.getAgentInformation()
171-
});
172-
}
173-
174-
175120
public playDialogue(
176121
scene: Phaser.Scene,
177122
text: string,
@@ -448,6 +393,9 @@ update() {
448393
}
449394

450395
private onClick(pointer: Phaser.Input.Pointer, gameObject: Phaser.GameObjects.GameObject) {
396+
397+
recorder.recordEvent("agent_clicked");
398+
451399
if (gameObject !== this) return;
452400
if (this.isBiased) return;
453401

src/game/utils/EventRecorder.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// EventRecorder.ts
2+
export class EventRecorder {
3+
private startTime: number | null = null;
4+
private events: { time: number; log: string }[] = [];
5+
6+
startRecord() {
7+
this.startTime = Date.now();
8+
this.events = [];
9+
console.log("Recording started at", new Date(this.startTime).toLocaleString());
10+
}
11+
12+
recordEvent(log: string) {
13+
if (this.startTime === null) {
14+
console.warn("Recording has not started. Call startRecord() first.");
15+
return;
16+
}
17+
const currentTime = Date.now();
18+
const relativeTime = (currentTime - this.startTime) / 1000;
19+
this.events.push({ time: relativeTime, log });
20+
console.log(`Event recorded: [${relativeTime.toFixed(2)}s]; ${log}`);
21+
}
22+
23+
endRecord(fileName: string = "events.csv") {
24+
if (this.startTime === null) {
25+
console.warn("Recording was never started.");
26+
return;
27+
}
28+
29+
const csvHeader = "time,log\n";
30+
const csvRows = this.events
31+
.map(e => `${e.time.toFixed(2)},${e.log.replace(/,/g, ";")}`)
32+
.join("\n");
33+
34+
const csvContent = csvHeader + csvRows;
35+
36+
// 生成 Blob 并下载
37+
const blob = new Blob([csvContent], { type: "text/csv" });
38+
const url = URL.createObjectURL(blob);
39+
const a = document.createElement("a");
40+
a.href = url;
41+
a.download = fileName;
42+
a.click();
43+
URL.revokeObjectURL(url);
44+
45+
console.log("CSV download triggered:", fileName);
46+
}
47+
48+
}

src/game/utils/recorder.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { EventRecorder } from "./EventRecorder";
2+
3+
export const recorder = new EventRecorder();

src/langgraph/agents.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
kidneyStatLevel2,
2020
kidneyStatLevel3
2121
} from '../const';
22+
import { recorder } from "../game/utils/recorder";
2223

2324
function hallucinationByType(t?: string) {
2425
switch (t) {
@@ -131,6 +132,7 @@ export async function createReport(
131132
reportBtn.on("pointerdown", () => {
132133
EventBus.emit("open-report", { department: zoneName+"-"+index });
133134
console.log("report button clicked", zoneName+"-"+index);
135+
recorder.recordEvent(`report_clicked_${zoneName}-${index}`);
134136
});
135137

136138
if (!scene.reportIcons) scene.reportIcons = [];

src/utils/interactionUtils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { recorder } from "../game/utils/recorder";
2+
13
export function createHoveredWindow(
24
scene: any,
35
pointer: any,
@@ -66,5 +68,6 @@ export function addEventToStrategy(
6668
scene.registry.set("workflowConfig", tempConfig);
6769
btn.setTexture(strategy);
6870
console.log("Updated workflowConfig:", scene.registry.get("workflowConfig"));
71+
recorder.recordEvent(`strategy_selected_${strategy}`); // Log event when strategy is selected
6972
});
7073
}

0 commit comments

Comments
 (0)