@gui-chat-plugin/othello 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -0
- package/dist/core/definition.d.ts +7 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/logic.d.ts +5 -0
- package/dist/core/plugin.d.ts +9 -0
- package/dist/core/samples.d.ts +5 -0
- package/dist/core/types.d.ts +77 -0
- package/dist/core.cjs +1 -0
- package/dist/core.js +10 -0
- package/dist/index.cjs +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +291 -0
- package/dist/style.css +1 -0
- package/dist/vue/Preview.vue.d.ts +8 -0
- package/dist/vue/View.vue.d.ts +9 -0
- package/dist/vue/index.d.ts +17 -0
- package/dist/vue.cjs +1 -0
- package/dist/vue.js +163 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @gui-chat-plugin/othello
|
|
2
|
+
|
|
3
|
+
Othello/Reversi game plugin for GUI Chat applications. Play Othello against an AI assistant or let it guide you through the game.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Full Othello/Reversi game implementation
|
|
8
|
+
- Interactive board with clickable moves
|
|
9
|
+
- Legal move indicators
|
|
10
|
+
- Turn tracking (user vs computer)
|
|
11
|
+
- Game end detection and winner announcement
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @gui-chat-plugin/othello
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Vue Integration
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { plugin } from "@gui-chat-plugin/othello/vue";
|
|
25
|
+
import "@gui-chat-plugin/othello/style.css";
|
|
26
|
+
|
|
27
|
+
// Register the plugin with your GUI Chat application
|
|
28
|
+
registerPlugin(plugin);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Core-only Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { executeOthello, playOthello, TOOL_DEFINITION } from "@gui-chat-plugin/othello";
|
|
35
|
+
|
|
36
|
+
// Start a new game
|
|
37
|
+
const result = await executeOthello(context, {
|
|
38
|
+
action: "new_game",
|
|
39
|
+
firstPlayer: "user",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Use the game logic directly
|
|
43
|
+
import { playOthello } from "@gui-chat-plugin/othello";
|
|
44
|
+
|
|
45
|
+
const state = playOthello({
|
|
46
|
+
action: "new_game",
|
|
47
|
+
playerNames: { B: "user", W: "computer" },
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## API
|
|
52
|
+
|
|
53
|
+
### OthelloArgs
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
interface OthelloArgs {
|
|
57
|
+
action: "new_game" | "move" | "pass";
|
|
58
|
+
col?: number; // 0-7 for move action
|
|
59
|
+
row?: number; // 0-7 for move action
|
|
60
|
+
board?: Cell[][]; // Current board state
|
|
61
|
+
currentSide?: Side; // "B" or "W"
|
|
62
|
+
playerNames?: { B: OthelloPlayerType; W: OthelloPlayerType };
|
|
63
|
+
firstPlayer?: OthelloPlayerType; // "user" or "computer"
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### OthelloState
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
interface OthelloState {
|
|
71
|
+
board: OthelloBoard; // 8x8 grid of ".", "B", or "W"
|
|
72
|
+
currentSide: Side; // Whose turn is next
|
|
73
|
+
playerNames: { B: string; W: string };
|
|
74
|
+
legalMoves: { row: number; col: number }[];
|
|
75
|
+
counts: { B: number; W: number; empty: number };
|
|
76
|
+
isTerminal: boolean;
|
|
77
|
+
winner: Side | "draw" | null;
|
|
78
|
+
lastAction: { type: "new_game" | "move" | "pass"; ... };
|
|
79
|
+
error?: string;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Install dependencies
|
|
87
|
+
yarn install
|
|
88
|
+
|
|
89
|
+
# Run demo
|
|
90
|
+
yarn dev
|
|
91
|
+
|
|
92
|
+
# Build
|
|
93
|
+
yarn build
|
|
94
|
+
|
|
95
|
+
# Lint
|
|
96
|
+
yarn lint
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Othello Plugin - Tool Definition
|
|
3
|
+
*/
|
|
4
|
+
import type { ToolDefinition } from "gui-chat-protocol";
|
|
5
|
+
export declare const TOOL_NAME = "playOthello";
|
|
6
|
+
export declare const TOOL_DEFINITION: ToolDefinition;
|
|
7
|
+
export declare const SYSTEM_PROMPT = "You can play Othello/Reversi with users. When a user wants to play:\n1. Start a new game with the \"new_game\" action\n2. For moves, the user specifies column (A-H) and row (1-8)\n3. Use the \"move\" action with the current board state\n4. Pass the current board state as-is - the game logic handles piece placement and flipping";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Othello Plugin - Core Entry Point
|
|
3
|
+
*/
|
|
4
|
+
export type { Cell, Side, OthelloBoard, OthelloCellValue, OthelloPlayerType, OthelloArgs, OthelloState, Command, NewGameCommand, MoveCommand, } from "./types";
|
|
5
|
+
export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, executeOthello, pluginCore, playOthello, } from "./plugin";
|
|
6
|
+
export { samples } from "./samples";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Othello Plugin Core
|
|
3
|
+
*/
|
|
4
|
+
import type { ToolPluginCore, ToolContext, ToolResult } from "gui-chat-protocol";
|
|
5
|
+
import type { OthelloArgs, OthelloState } from "./types";
|
|
6
|
+
export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT } from "./definition";
|
|
7
|
+
export { playOthello } from "./logic";
|
|
8
|
+
export declare const executeOthello: (_context: ToolContext, args: OthelloArgs) => Promise<ToolResult<never, OthelloState>>;
|
|
9
|
+
export declare const pluginCore: ToolPluginCore<never, OthelloState, OthelloArgs>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Othello Plugin - Type Definitions
|
|
3
|
+
*/
|
|
4
|
+
export type Cell = "." | "B" | "W";
|
|
5
|
+
export type Side = "B" | "W";
|
|
6
|
+
export type OthelloBoard = Cell[][];
|
|
7
|
+
export type OthelloCellValue = Cell;
|
|
8
|
+
export type OthelloPlayerType = "user" | "computer";
|
|
9
|
+
export interface OthelloArgs {
|
|
10
|
+
action: "new_game" | "move" | "pass";
|
|
11
|
+
col?: number;
|
|
12
|
+
row?: number;
|
|
13
|
+
board?: OthelloCellValue[][];
|
|
14
|
+
currentSide?: Side;
|
|
15
|
+
playerNames?: {
|
|
16
|
+
B: OthelloPlayerType;
|
|
17
|
+
W: OthelloPlayerType;
|
|
18
|
+
};
|
|
19
|
+
firstPlayer?: OthelloPlayerType;
|
|
20
|
+
}
|
|
21
|
+
export type NewGameCommand = {
|
|
22
|
+
action: "new_game";
|
|
23
|
+
playerNames: {
|
|
24
|
+
B: string;
|
|
25
|
+
W: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type MoveCommand = {
|
|
29
|
+
action: "move";
|
|
30
|
+
row: number;
|
|
31
|
+
col: number;
|
|
32
|
+
board: OthelloBoard;
|
|
33
|
+
currentSide: Side;
|
|
34
|
+
playerNames: {
|
|
35
|
+
B: string;
|
|
36
|
+
W: string;
|
|
37
|
+
};
|
|
38
|
+
} | {
|
|
39
|
+
action: "pass";
|
|
40
|
+
board: OthelloBoard;
|
|
41
|
+
currentSide: Side;
|
|
42
|
+
playerNames: {
|
|
43
|
+
B: string;
|
|
44
|
+
W: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
export type Command = NewGameCommand | MoveCommand;
|
|
48
|
+
export interface OthelloState {
|
|
49
|
+
board: OthelloBoard;
|
|
50
|
+
currentSide: Side;
|
|
51
|
+
playerNames: {
|
|
52
|
+
B: string;
|
|
53
|
+
W: string;
|
|
54
|
+
};
|
|
55
|
+
legalMoves: {
|
|
56
|
+
row: number;
|
|
57
|
+
col: number;
|
|
58
|
+
}[];
|
|
59
|
+
counts: {
|
|
60
|
+
B: number;
|
|
61
|
+
W: number;
|
|
62
|
+
empty: number;
|
|
63
|
+
};
|
|
64
|
+
isTerminal: boolean;
|
|
65
|
+
winner: Side | "draw" | null;
|
|
66
|
+
lastAction: {
|
|
67
|
+
type: "new_game";
|
|
68
|
+
} | {
|
|
69
|
+
type: "move";
|
|
70
|
+
row: number;
|
|
71
|
+
col: number;
|
|
72
|
+
flipped: number;
|
|
73
|
+
} | {
|
|
74
|
+
type: "pass";
|
|
75
|
+
};
|
|
76
|
+
error?: string;
|
|
77
|
+
}
|
package/dist/core.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index.cjs");exports.SYSTEM_PROMPT=e.SYSTEM_PROMPT;exports.TOOL_DEFINITION=e.TOOL_DEFINITION;exports.TOOL_NAME=e.TOOL_NAME;exports.executeOthello=e.executeOthello;exports.playOthello=e.playOthello;exports.pluginCore=e.pluginCore;exports.samples=e.samples;
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SYSTEM_PROMPT as l, TOOL_DEFINITION as o, TOOL_NAME as T, executeOthello as p, playOthello as t, pluginCore as r, samples as E } from "./index.js";
|
|
2
|
+
export {
|
|
3
|
+
l as SYSTEM_PROMPT,
|
|
4
|
+
o as TOOL_DEFINITION,
|
|
5
|
+
T as TOOL_NAME,
|
|
6
|
+
p as executeOthello,
|
|
7
|
+
t as playOthello,
|
|
8
|
+
r as pluginCore,
|
|
9
|
+
E as samples
|
|
10
|
+
};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const S="playOthello",B={type:"function",name:S,description:"Play Othello/Reversi game with the user. You can start a new game, make moves, or pass turns.",parameters:{type:"object",properties:{action:{type:"string",enum:["new_game","move","pass"],description:"The action to perform: start a new game, make a move, or pass the turn"},col:{type:"number",description:"Column position for the move (0-7, required for 'move' action). The user will tell you the column by specifying A to H",minimum:0,maximum:7},row:{type:"number",description:"Row position for the move (0-7, required for 'move' action). The user will tell you the row by specifying 1 to 8",minimum:0,maximum:7},board:{type:"array",description:"Current 8x8 board state BEFORE the move (required for 'move' and 'pass' actions). IMPORTANT: Do NOT modify the board yourself - pass the current board state as-is, and the game logic will handle placing the piece and flipping opponent pieces.",items:{type:"array",items:{type:"string",enum:[".","B","W"]}}},currentSide:{type:"string",enum:["B","W"],description:"Current player's side (required for 'move' and 'pass' actions)"},playerNames:{type:"object",description:"Player assignments (required for 'move' and 'pass' actions)",properties:{B:{type:"string",enum:["user","computer"]},W:{type:"string",enum:["user","computer"]}},required:["B","W"]},firstPlayer:{type:"string",enum:["user","computer"],description:"Optional: Which player should play as Black (goes first) for 'new_game' action. If not specified, will be chosen randomly."}},required:["action"],additionalProperties:!1}},P=`You can play Othello/Reversi with users. When a user wants to play:
|
|
2
|
+
1. Start a new game with the "new_game" action
|
|
3
|
+
2. For moves, the user specifies column (A-H) and row (1-8)
|
|
4
|
+
3. Use the "move" action with the current board state
|
|
5
|
+
4. Pass the current board state as-is - the game logic handles piece placement and flipping`,k=[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];function $(){const r=[];for(let e=0;e<8;e++)r[e]=new Array(8).fill(".");return r}function C(){const r=$();return r[3][3]="W",r[3][4]="B",r[4][3]="B",r[4][4]="W",r}function x(r){return r.map(e=>[...e])}function O(r,e){return r>=0&&r<8&&e>=0&&e<8}function w(r){return r==="B"?"W":"B"}function R(r,e,o,t,n,a){const i=w(a),s=[];let l=e+t,c=o+n;for(;O(l,c)&&r[l][c]===i;)s.push({row:l,col:c}),l+=t,c+=n;return O(l,c)&&r[l][c]===a&&s.length>0?s:[]}function M(r,e,o,t){if(r[e][o]!==".")return[];const n=[];for(const[a,i]of k){const s=R(r,e,o,a,i,t);n.push(...s)}return n}function A(r,e,o,t){return M(r,e,o,t).length>0}function d(r,e){const o=[];for(let t=0;t<8;t++)for(let n=0;n<8;n++)A(r,t,n,e)&&o.push({row:t,col:n});return o}function q(r,e,o,t){const n=x(r),a=M(r,e,o,t);n[e][o]=t;for(const{row:i,col:s}of a)n[i][s]=t;return{newBoard:n,flippedCount:a.length}}function y(r){let e=0,o=0,t=0;for(let n=0;n<8;n++)for(let a=0;a<8;a++){const i=r[n][a];i==="B"?e++:i==="W"?o++:t++}return{B:e,W:o,empty:t}}function N(r){return r.empty>0?null:r.B>r.W?"B":r.W>r.B?"W":"draw"}function _(r){if(r.action==="new_game"){const{playerNames:u}=r,m=C(),h=d(m,"B"),f=y(m);return{board:m,currentSide:"B",playerNames:u,legalMoves:h,counts:f,isTerminal:!1,winner:null,lastAction:{type:"new_game"}}}if(r.action==="pass"){const{board:u,currentSide:m,playerNames:h}=r,f=w(m),v=d(u,f),b=y(u),T=v.length===0,I=T?N(b):null;return{board:u,currentSide:f,playerNames:h,legalMoves:v,counts:b,isTerminal:T,winner:I,lastAction:{type:"pass"}}}const{row:e,col:o,board:t,currentSide:n,playerNames:a}=r;if(!A(t,e,o,n)){const u=d(t,n),m=y(t);return{board:t,currentSide:n,playerNames:a,legalMoves:u,counts:m,isTerminal:!1,winner:null,lastAction:{type:"pass"},error:`Invalid move: (${e}, ${o}) is not a legal move for ${n}`}}const{newBoard:i,flippedCount:s}=q(t,e,o,n),l=w(n),c=d(i,l),p=y(i),g=c.length===0&&d(i,n).length===0,E=g?N(p):null;return{board:i,currentSide:l,playerNames:a,legalMoves:c,counts:p,isTerminal:g,winner:E,lastAction:{type:"move",row:e,col:o,flipped:s}}}const W=async(r,e)=>{try{let o;if(e.action==="new_game"){let s;e.firstPlayer?s=e.firstPlayer:s=Math.random()<.5?"computer":"user",o={action:"new_game",playerNames:{B:s,W:s==="user"?"computer":"user"}}}else if(e.action==="move"){if(typeof e.row!="number"||typeof e.col!="number"||!e.board||!e.currentSide||!e.playerNames)throw new Error("Move action requires row, col, board, currentSide, and playerNames parameters");o={action:"move",row:e.row,col:e.col,board:e.board,currentSide:e.currentSide,playerNames:e.playerNames}}else if(e.action==="pass"){if(!e.board||!e.currentSide||!e.playerNames)throw new Error("Pass action requires board, currentSide, and playerNames parameters");o={action:"pass",board:e.board,currentSide:e.currentSide,playerNames:e.playerNames}}else throw new Error(`Unknown action: ${e.action}`);const t=_(o);if(t.error){const s=t.playerNames[t.currentSide]==="computer",l=t.legalMoves.map(p=>`(${p.row}, ${p.col})`).join(", "),c=s?`Invalid move attempted. You must make a valid move. Legal moves are: ${l}. Choose one of these moves.`:`Invalid move attempted. Tell the user they must make a valid move. Legal moves are: ${l}. The user will tell you the move by specifying column (A to H) and row (1 to 8).`;return{message:t.error,jsonData:t,instructions:c,updating:!0}}let n="";t.lastAction.type==="new_game"?n="Started a new Othello game! Black (●) goes first.":t.lastAction.type==="move"?n=`Played at (${t.lastAction.row}, ${t.lastAction.col}) and flipped ${t.lastAction.flipped} pieces.`:t.lastAction.type==="pass"&&(n="Passed the turn."),t.isTerminal&&(t.winner==="draw"?n+=" Game over - it's a draw!":t.winner&&(n+=` Game over - ${t.winner==="B"?"Black":"White"} wins!`));const a=t.playerNames[t.currentSide]==="computer",i=t.isTerminal?"The game is over. Announce the game result.":a?"The game state has been updated. Do not describe the state of the game. It is assistant's turn. You MUSK choose your next move.":"The game state has been updated. Tell the user to make a move. Do not describe the state of the game. The user is able to see it. The user will tell you the move by specifying colum (A to H) and row (1 to 8)";return{message:n,jsonData:t,instructions:i,instructionsRequired:t.isTerminal||a,updating:e.action!=="new_game"}}catch(o){return console.error(`ERR: exception
|
|
6
|
+
Othello game error`,o),{message:`Othello game error: ${o instanceof Error?o.message:"Unknown error"}`,instructions:"Acknowledge that there was an error with the Othello game and suggest trying again."}}},D={toolDefinition:B,execute:W,generatingMessage:"Processing Othello move...",isEnabled:()=>!0,systemPrompt:P},F=[{name:"New Game (User First)",args:{action:"new_game",firstPlayer:"user"}},{name:"New Game (Computer First)",args:{action:"new_game",firstPlayer:"computer"}}];exports.SYSTEM_PROMPT=P;exports.TOOL_DEFINITION=B;exports.TOOL_NAME=S;exports.executeOthello=W;exports.playOthello=_;exports.pluginCore=D;exports.samples=F;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Othello Plugin - Main Entry Point
|
|
3
|
+
*/
|
|
4
|
+
export type { Cell, Side, OthelloBoard, OthelloCellValue, OthelloPlayerType, OthelloArgs, OthelloState, Command, NewGameCommand, MoveCommand, } from "./core/types";
|
|
5
|
+
export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, executeOthello, pluginCore, playOthello, } from "./core/plugin";
|
|
6
|
+
export { samples } from "./core/samples";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
const A = "playOthello", W = {
|
|
2
|
+
type: "function",
|
|
3
|
+
name: A,
|
|
4
|
+
description: "Play Othello/Reversi game with the user. You can start a new game, make moves, or pass turns.",
|
|
5
|
+
parameters: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
action: {
|
|
9
|
+
type: "string",
|
|
10
|
+
enum: ["new_game", "move", "pass"],
|
|
11
|
+
description: "The action to perform: start a new game, make a move, or pass the turn"
|
|
12
|
+
},
|
|
13
|
+
col: {
|
|
14
|
+
type: "number",
|
|
15
|
+
description: "Column position for the move (0-7, required for 'move' action). The user will tell you the column by specifying A to H",
|
|
16
|
+
minimum: 0,
|
|
17
|
+
maximum: 7
|
|
18
|
+
},
|
|
19
|
+
row: {
|
|
20
|
+
type: "number",
|
|
21
|
+
description: "Row position for the move (0-7, required for 'move' action). The user will tell you the row by specifying 1 to 8",
|
|
22
|
+
minimum: 0,
|
|
23
|
+
maximum: 7
|
|
24
|
+
},
|
|
25
|
+
board: {
|
|
26
|
+
type: "array",
|
|
27
|
+
description: "Current 8x8 board state BEFORE the move (required for 'move' and 'pass' actions). IMPORTANT: Do NOT modify the board yourself - pass the current board state as-is, and the game logic will handle placing the piece and flipping opponent pieces.",
|
|
28
|
+
items: {
|
|
29
|
+
type: "array",
|
|
30
|
+
items: {
|
|
31
|
+
type: "string",
|
|
32
|
+
enum: [".", "B", "W"]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
currentSide: {
|
|
37
|
+
type: "string",
|
|
38
|
+
enum: ["B", "W"],
|
|
39
|
+
description: "Current player's side (required for 'move' and 'pass' actions)"
|
|
40
|
+
},
|
|
41
|
+
playerNames: {
|
|
42
|
+
type: "object",
|
|
43
|
+
description: "Player assignments (required for 'move' and 'pass' actions)",
|
|
44
|
+
properties: {
|
|
45
|
+
B: {
|
|
46
|
+
type: "string",
|
|
47
|
+
enum: ["user", "computer"]
|
|
48
|
+
},
|
|
49
|
+
W: {
|
|
50
|
+
type: "string",
|
|
51
|
+
enum: ["user", "computer"]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
required: ["B", "W"]
|
|
55
|
+
},
|
|
56
|
+
firstPlayer: {
|
|
57
|
+
type: "string",
|
|
58
|
+
enum: ["user", "computer"],
|
|
59
|
+
description: "Optional: Which player should play as Black (goes first) for 'new_game' action. If not specified, will be chosen randomly."
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
required: ["action"],
|
|
63
|
+
additionalProperties: !1
|
|
64
|
+
}
|
|
65
|
+
}, _ = `You can play Othello/Reversi with users. When a user wants to play:
|
|
66
|
+
1. Start a new game with the "new_game" action
|
|
67
|
+
2. For moves, the user specifies column (A-H) and row (1-8)
|
|
68
|
+
3. Use the "move" action with the current board state
|
|
69
|
+
4. Pass the current board state as-is - the game logic handles piece placement and flipping`, k = [
|
|
70
|
+
[-1, -1],
|
|
71
|
+
[-1, 0],
|
|
72
|
+
[-1, 1],
|
|
73
|
+
[0, -1],
|
|
74
|
+
[0, 1],
|
|
75
|
+
[1, -1],
|
|
76
|
+
[1, 0],
|
|
77
|
+
[1, 1]
|
|
78
|
+
];
|
|
79
|
+
function E() {
|
|
80
|
+
const n = [];
|
|
81
|
+
for (let e = 0; e < 8; e++)
|
|
82
|
+
n[e] = new Array(8).fill(".");
|
|
83
|
+
return n;
|
|
84
|
+
}
|
|
85
|
+
function I() {
|
|
86
|
+
const n = E();
|
|
87
|
+
return n[3][3] = "W", n[3][4] = "B", n[4][3] = "B", n[4][4] = "W", n;
|
|
88
|
+
}
|
|
89
|
+
function $(n) {
|
|
90
|
+
return n.map((e) => [...e]);
|
|
91
|
+
}
|
|
92
|
+
function B(n, e) {
|
|
93
|
+
return n >= 0 && n < 8 && e >= 0 && e < 8;
|
|
94
|
+
}
|
|
95
|
+
function w(n) {
|
|
96
|
+
return n === "B" ? "W" : "B";
|
|
97
|
+
}
|
|
98
|
+
function x(n, e, o, t, r, a) {
|
|
99
|
+
const i = w(a), s = [];
|
|
100
|
+
let l = e + t, c = o + r;
|
|
101
|
+
for (; B(l, c) && n[l][c] === i; )
|
|
102
|
+
s.push({ row: l, col: c }), l += t, c += r;
|
|
103
|
+
return B(l, c) && n[l][c] === a && s.length > 0 ? s : [];
|
|
104
|
+
}
|
|
105
|
+
function S(n, e, o, t) {
|
|
106
|
+
if (n[e][o] !== ".")
|
|
107
|
+
return [];
|
|
108
|
+
const r = [];
|
|
109
|
+
for (const [a, i] of k) {
|
|
110
|
+
const s = x(n, e, o, a, i, t);
|
|
111
|
+
r.push(...s);
|
|
112
|
+
}
|
|
113
|
+
return r;
|
|
114
|
+
}
|
|
115
|
+
function O(n, e, o, t) {
|
|
116
|
+
return S(n, e, o, t).length > 0;
|
|
117
|
+
}
|
|
118
|
+
function d(n, e) {
|
|
119
|
+
const o = [];
|
|
120
|
+
for (let t = 0; t < 8; t++)
|
|
121
|
+
for (let r = 0; r < 8; r++)
|
|
122
|
+
O(n, t, r, e) && o.push({ row: t, col: r });
|
|
123
|
+
return o;
|
|
124
|
+
}
|
|
125
|
+
function C(n, e, o, t) {
|
|
126
|
+
const r = $(n), a = S(n, e, o, t);
|
|
127
|
+
r[e][o] = t;
|
|
128
|
+
for (const { row: i, col: s } of a)
|
|
129
|
+
r[i][s] = t;
|
|
130
|
+
return { newBoard: r, flippedCount: a.length };
|
|
131
|
+
}
|
|
132
|
+
function y(n) {
|
|
133
|
+
let e = 0, o = 0, t = 0;
|
|
134
|
+
for (let r = 0; r < 8; r++)
|
|
135
|
+
for (let a = 0; a < 8; a++) {
|
|
136
|
+
const i = n[r][a];
|
|
137
|
+
i === "B" ? e++ : i === "W" ? o++ : t++;
|
|
138
|
+
}
|
|
139
|
+
return { B: e, W: o, empty: t };
|
|
140
|
+
}
|
|
141
|
+
function N(n) {
|
|
142
|
+
return n.empty > 0 ? null : n.B > n.W ? "B" : n.W > n.B ? "W" : "draw";
|
|
143
|
+
}
|
|
144
|
+
function q(n) {
|
|
145
|
+
if (n.action === "new_game") {
|
|
146
|
+
const { playerNames: u } = n, m = I(), h = d(m, "B"), f = y(m);
|
|
147
|
+
return {
|
|
148
|
+
board: m,
|
|
149
|
+
currentSide: "B",
|
|
150
|
+
playerNames: u,
|
|
151
|
+
legalMoves: h,
|
|
152
|
+
counts: f,
|
|
153
|
+
isTerminal: !1,
|
|
154
|
+
winner: null,
|
|
155
|
+
lastAction: { type: "new_game" }
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (n.action === "pass") {
|
|
159
|
+
const { board: u, currentSide: m, playerNames: h } = n, f = w(m), v = d(u, f), b = y(u), T = v.length === 0, M = T ? N(b) : null;
|
|
160
|
+
return {
|
|
161
|
+
board: u,
|
|
162
|
+
currentSide: f,
|
|
163
|
+
playerNames: h,
|
|
164
|
+
legalMoves: v,
|
|
165
|
+
counts: b,
|
|
166
|
+
isTerminal: T,
|
|
167
|
+
winner: M,
|
|
168
|
+
lastAction: { type: "pass" }
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const { row: e, col: o, board: t, currentSide: r, playerNames: a } = n;
|
|
172
|
+
if (!O(t, e, o, r)) {
|
|
173
|
+
const u = d(t, r), m = y(t);
|
|
174
|
+
return {
|
|
175
|
+
board: t,
|
|
176
|
+
currentSide: r,
|
|
177
|
+
playerNames: a,
|
|
178
|
+
legalMoves: u,
|
|
179
|
+
counts: m,
|
|
180
|
+
isTerminal: !1,
|
|
181
|
+
winner: null,
|
|
182
|
+
lastAction: { type: "pass" },
|
|
183
|
+
// Keep previous action type
|
|
184
|
+
error: `Invalid move: (${e}, ${o}) is not a legal move for ${r}`
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const { newBoard: i, flippedCount: s } = C(t, e, o, r), l = w(r), c = d(i, l), p = y(i), g = c.length === 0 && d(i, r).length === 0, P = g ? N(p) : null;
|
|
188
|
+
return {
|
|
189
|
+
board: i,
|
|
190
|
+
currentSide: l,
|
|
191
|
+
playerNames: a,
|
|
192
|
+
legalMoves: c,
|
|
193
|
+
counts: p,
|
|
194
|
+
isTerminal: g,
|
|
195
|
+
winner: P,
|
|
196
|
+
lastAction: { type: "move", row: e, col: o, flipped: s }
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const R = async (n, e) => {
|
|
200
|
+
try {
|
|
201
|
+
let o;
|
|
202
|
+
if (e.action === "new_game") {
|
|
203
|
+
let s;
|
|
204
|
+
e.firstPlayer ? s = e.firstPlayer : s = Math.random() < 0.5 ? "computer" : "user", o = {
|
|
205
|
+
action: "new_game",
|
|
206
|
+
playerNames: { B: s, W: s === "user" ? "computer" : "user" }
|
|
207
|
+
};
|
|
208
|
+
} else if (e.action === "move") {
|
|
209
|
+
if (typeof e.row != "number" || typeof e.col != "number" || !e.board || !e.currentSide || !e.playerNames)
|
|
210
|
+
throw new Error(
|
|
211
|
+
"Move action requires row, col, board, currentSide, and playerNames parameters"
|
|
212
|
+
);
|
|
213
|
+
o = {
|
|
214
|
+
action: "move",
|
|
215
|
+
row: e.row,
|
|
216
|
+
col: e.col,
|
|
217
|
+
board: e.board,
|
|
218
|
+
currentSide: e.currentSide,
|
|
219
|
+
playerNames: e.playerNames
|
|
220
|
+
};
|
|
221
|
+
} else if (e.action === "pass") {
|
|
222
|
+
if (!e.board || !e.currentSide || !e.playerNames)
|
|
223
|
+
throw new Error(
|
|
224
|
+
"Pass action requires board, currentSide, and playerNames parameters"
|
|
225
|
+
);
|
|
226
|
+
o = {
|
|
227
|
+
action: "pass",
|
|
228
|
+
board: e.board,
|
|
229
|
+
currentSide: e.currentSide,
|
|
230
|
+
playerNames: e.playerNames
|
|
231
|
+
};
|
|
232
|
+
} else
|
|
233
|
+
throw new Error(`Unknown action: ${e.action}`);
|
|
234
|
+
const t = q(o);
|
|
235
|
+
if (t.error) {
|
|
236
|
+
const s = t.playerNames[t.currentSide] === "computer", l = t.legalMoves.map((p) => `(${p.row}, ${p.col})`).join(", "), c = s ? `Invalid move attempted. You must make a valid move. Legal moves are: ${l}. Choose one of these moves.` : `Invalid move attempted. Tell the user they must make a valid move. Legal moves are: ${l}. The user will tell you the move by specifying column (A to H) and row (1 to 8).`;
|
|
237
|
+
return {
|
|
238
|
+
message: t.error,
|
|
239
|
+
jsonData: t,
|
|
240
|
+
instructions: c,
|
|
241
|
+
updating: !0
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
let r = "";
|
|
245
|
+
t.lastAction.type === "new_game" ? r = "Started a new Othello game! Black (●) goes first." : t.lastAction.type === "move" ? r = `Played at (${t.lastAction.row}, ${t.lastAction.col}) and flipped ${t.lastAction.flipped} pieces.` : t.lastAction.type === "pass" && (r = "Passed the turn."), t.isTerminal && (t.winner === "draw" ? r += " Game over - it's a draw!" : t.winner && (r += ` Game over - ${t.winner === "B" ? "Black" : "White"} wins!`));
|
|
246
|
+
const a = t.playerNames[t.currentSide] === "computer", i = t.isTerminal ? "The game is over. Announce the game result." : a ? "The game state has been updated. Do not describe the state of the game. It is assistant's turn. You MUSK choose your next move." : "The game state has been updated. Tell the user to make a move. Do not describe the state of the game. The user is able to see it. The user will tell you the move by specifying colum (A to H) and row (1 to 8)";
|
|
247
|
+
return {
|
|
248
|
+
message: r,
|
|
249
|
+
jsonData: t,
|
|
250
|
+
instructions: i,
|
|
251
|
+
instructionsRequired: t.isTerminal || a,
|
|
252
|
+
updating: e.action !== "new_game"
|
|
253
|
+
};
|
|
254
|
+
} catch (o) {
|
|
255
|
+
return console.error(`ERR: exception
|
|
256
|
+
Othello game error`, o), {
|
|
257
|
+
message: `Othello game error: ${o instanceof Error ? o.message : "Unknown error"}`,
|
|
258
|
+
instructions: "Acknowledge that there was an error with the Othello game and suggest trying again."
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}, D = {
|
|
262
|
+
toolDefinition: W,
|
|
263
|
+
execute: R,
|
|
264
|
+
generatingMessage: "Processing Othello move...",
|
|
265
|
+
isEnabled: () => !0,
|
|
266
|
+
systemPrompt: _
|
|
267
|
+
}, F = [
|
|
268
|
+
{
|
|
269
|
+
name: "New Game (User First)",
|
|
270
|
+
args: {
|
|
271
|
+
action: "new_game",
|
|
272
|
+
firstPlayer: "user"
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "New Game (Computer First)",
|
|
277
|
+
args: {
|
|
278
|
+
action: "new_game",
|
|
279
|
+
firstPlayer: "computer"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
];
|
|
283
|
+
export {
|
|
284
|
+
_ as SYSTEM_PROMPT,
|
|
285
|
+
W as TOOL_DEFINITION,
|
|
286
|
+
A as TOOL_NAME,
|
|
287
|
+
R as executeOthello,
|
|
288
|
+
q as playOthello,
|
|
289
|
+
D as pluginCore,
|
|
290
|
+
F as samples
|
|
291
|
+
};
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-green-50:oklch(98.2% .018 155.826);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-200:oklch(87% .065 274.039);--color-indigo-700:oklch(45.7% .24 277.023);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.mx-auto{margin-inline:auto}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-5{margin-bottom:calc(var(--spacing)*5)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.contents{display:contents}.flex{display:flex}.grid{display:grid}.inline-block{display:inline-block}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-\[500px\]{height:500px}.h-full{height:100%}.w-3{width:calc(var(--spacing)*3)}.w-4{width:calc(var(--spacing)*4)}.w-10{width:calc(var(--spacing)*10)}.w-12{width:calc(var(--spacing)*12)}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[200px\]{max-width:200px}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-2{gap:calc(var(--spacing)*2)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-gray-600{border-color:var(--color-gray-600)}.border-green-900{border-color:var(--color-green-900)}.border-indigo-200{border-color:var(--color-indigo-200)}.bg-black{background-color:var(--color-black)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-600{background-color:var(--color-green-600)}.bg-green-700{background-color:var(--color-green-700)}.bg-green-800{background-color:var(--color-green-800)}.bg-indigo-100{background-color:var(--color-indigo-100)}.bg-white{background-color:var(--color-white)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-8{padding:calc(var(--spacing)*8)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-2{padding-block:calc(var(--spacing)*2)}.text-center{text-align:center}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.text-gray-200{color:var(--color-gray-200)}.text-gray-300{color:var(--color-gray-300)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-indigo-700{color:var(--color-indigo-700)}.text-white{color:var(--color-white)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hover\:bg-green-600:hover{background-color:var(--color-green-600)}.hover\:bg-indigo-200:hover{background-color:var(--color-indigo-200)}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ToolResult } from "gui-chat-protocol/vue";
|
|
2
|
+
import type { OthelloState } from "../core/types";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
result: ToolResult<never, OthelloState>;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolResult } from "gui-chat-protocol/vue";
|
|
2
|
+
import type { OthelloState } from "../core/types";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
selectedResult: ToolResult<never, OthelloState> | null;
|
|
5
|
+
sendTextMessage: (text?: string) => void;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Othello Plugin - Vue Implementation
|
|
3
|
+
*/
|
|
4
|
+
import "../style.css";
|
|
5
|
+
import type { ToolPlugin } from "gui-chat-protocol/vue";
|
|
6
|
+
import type { OthelloArgs, OthelloState } from "../core/types";
|
|
7
|
+
import View from "./View.vue";
|
|
8
|
+
import Preview from "./Preview.vue";
|
|
9
|
+
export declare const plugin: ToolPlugin<never, OthelloState, OthelloArgs>;
|
|
10
|
+
export type { Cell, Side, OthelloBoard, OthelloCellValue, OthelloPlayerType, OthelloArgs, OthelloState, } from "../core/types";
|
|
11
|
+
export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, executeOthello, pluginCore, playOthello, } from "../core/plugin";
|
|
12
|
+
export { samples } from "../core/samples";
|
|
13
|
+
export { View, Preview };
|
|
14
|
+
declare const _default: {
|
|
15
|
+
plugin: ToolPlugin<never, OthelloState, OthelloArgs, import("gui-chat-protocol/vue").InputHandler, Record<string, unknown>>;
|
|
16
|
+
};
|
|
17
|
+
export default _default;
|
package/dist/vue.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const c=require("./index.cjs"),e=require("vue"),C={class:"w-full h-full flex flex-col items-center justify-center p-4"},E={key:0,class:"flex flex-col items-center"},w={class:"text-white text-lg font-bold mb-4 text-center"},N={class:"grid grid-cols-8 gap-0.5 p-4 bg-green-800 rounded-lg border-2 border-green-900"},M=["onClick","onMouseenter","onMouseleave"],x={key:1,class:"w-10 h-10 flex items-center justify-center text-gray-300 text-sm font-bold"},h=e.defineComponent({__name:"View",props:{selectedResult:{},sendTextMessage:{type:Function}},setup(a){const m=a,t=e.ref(null),s=e.ref(null);e.watch(()=>m.selectedResult,l=>{l?.toolName==="playOthello"&&l.jsonData&&(t.value=l.jsonData)},{immediate:!0});const g=e.computed(()=>{if(!t.value?.playerNames)return"";const l=t.value.playerNames[t.value.currentSide];return l.charAt(0).toUpperCase()+l.slice(1)}),v=e.computed(()=>t.value?t.value.currentSide==="B"?"Black":"White":""),i=e.computed(()=>t.value?.playerNames&&t.value.playerNames[t.value.currentSide]==="computer"),d=e.computed(()=>{if(!t.value?.board)return[];const l=[];for(let r=0;r<8;r++)for(let o=0;o<8;o++){const n=t.value.board[r][o],u=t.value.legalMoves?.some(k=>k.row===r&&k.col===o);l.push({row:r,col:o,piece:n!=="."?n:null,isLegalMove:u??!1,label:u?String.fromCharCode(65+o)+(r+1):""})}return l});function p(l,r){const o="w-12 h-12 flex items-center justify-center border border-green-900 bg-green-700",n=l.isLegalMove&&!i.value&&s.value===r?"bg-green-600":"",u=l.isLegalMove&&!i.value&&!t.value?.isTerminal?"cursor-pointer hover:bg-green-600":"cursor-default";return`${o} ${n} ${u}`}function _(l){return l==="B"?"bg-black":"bg-white"}function b(l){if(!t.value||t.value.isTerminal||i.value)return;const r=d.value[l];if(!r.isLegalMove)return;const o=String.fromCharCode(65+r.col),n=r.row+1;m.sendTextMessage(`I want to play at ${o}${n}, which is column=${r.col}, row=${r.row} `)}function f(l,r){!t.value||t.value.isTerminal||i.value||!d.value[l].isLegalMove||(s.value=r?l:null)}return(l,r)=>(e.openBlock(),e.createElementBlock("div",C,[t.value?(e.openBlock(),e.createElementBlock("div",E,[e.createElementVNode("div",w," Current Turn: "+e.toDisplayString(g.value)+" ("+e.toDisplayString(v.value)+") ",1),e.createElementVNode("div",N,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(d.value,(o,n)=>(e.openBlock(),e.createElementBlock("div",{key:n,class:e.normalizeClass(p(o,n)),onClick:u=>b(n),onMouseenter:u=>f(n,!0),onMouseleave:u=>f(n,!1)},[o.piece?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass([_(o.piece),"w-10 h-10 rounded-full border-2 border-gray-600"])},null,2)):o.isLegalMove&&!i.value?(e.openBlock(),e.createElementBlock("div",x,e.toDisplayString(o.label),1)):e.createCommentVNode("",!0)],42,M))),128))])])):e.createCommentVNode("",!0)]))}}),T={class:"p-3 bg-green-50 rounded"},$={key:0,class:"space-y-1"},O={class:"flex justify-center"},D={class:"inline-block",style:{"background-color":"#2d5016",padding:"1px"}},S={class:"grid grid-cols-8",style:{gap:"1px"}},j={key:0,class:"w-3 h-3 bg-black rounded-full"},L={key:1,class:"w-3 h-3 bg-white rounded-full"},V={class:"text-xs text-center space-y-1"},P={key:0,class:"text-gray-600"},F={key:1,class:"font-medium"},y=e.defineComponent({__name:"Preview",props:{result:{}},setup(a){function m(s){return s.isTerminal?s.winner==="draw"?"Draw!":s.winner==="B"?"⚫ Black Wins!":s.winner==="W"?"⚪ White Wins!":"Game Over":""}function t(s){return s.charAt(0).toUpperCase()+s.slice(1)}return(s,g)=>(e.openBlock(),e.createElementBlock("div",T,[a.result.jsonData?(e.openBlock(),e.createElementBlock("div",$,[e.createElementVNode("div",O,[e.createElementVNode("div",D,[e.createElementVNode("div",S,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(a.result.jsonData.board,(v,i)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:i},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(v,(d,p)=>(e.openBlock(),e.createElementBlock("div",{key:`${i}-${p}`,class:"w-4 h-4 flex items-center justify-center",style:{"background-color":"#3d6b20"}},[d==="B"?(e.openBlock(),e.createElementBlock("div",j)):d==="W"?(e.openBlock(),e.createElementBlock("div",L)):e.createCommentVNode("",!0)]))),128))],64))),128))])])]),e.createElementVNode("div",V,[a.result.jsonData.isTerminal?(e.openBlock(),e.createElementBlock("div",F,e.toDisplayString(m(a.result.jsonData)),1)):(e.openBlock(),e.createElementBlock("div",P,e.toDisplayString(a.result.jsonData.currentSide==="B"?"⚫":"⚪")+" "+e.toDisplayString(t(a.result.jsonData.playerNames[a.result.jsonData.currentSide]))+" to play ",1))])])):e.createCommentVNode("",!0)]))}}),B={...c.pluginCore,viewComponent:h,previewComponent:y,samples:c.samples},I={plugin:B};exports.SYSTEM_PROMPT=c.SYSTEM_PROMPT;exports.TOOL_DEFINITION=c.TOOL_DEFINITION;exports.TOOL_NAME=c.TOOL_NAME;exports.executeOthello=c.executeOthello;exports.playOthello=c.playOthello;exports.pluginCore=c.pluginCore;exports.samples=c.samples;exports.Preview=y;exports.View=h;exports.default=I;exports.plugin=B;
|
package/dist/vue.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { samples as T, pluginCore as D } from "./index.js";
|
|
2
|
+
import { SYSTEM_PROMPT as re, TOOL_DEFINITION as se, TOOL_NAME as le, executeOthello as ne, playOthello as oe } from "./index.js";
|
|
3
|
+
import { defineComponent as M, ref as x, watch as L, computed as g, createElementBlock as l, openBlock as n, createCommentVNode as p, createElementVNode as v, toDisplayString as f, Fragment as h, renderList as b, normalizeClass as $ } from "vue";
|
|
4
|
+
const B = { class: "w-full h-full flex flex-col items-center justify-center p-4" }, O = {
|
|
5
|
+
key: 0,
|
|
6
|
+
class: "flex flex-col items-center"
|
|
7
|
+
}, S = { class: "text-white text-lg font-bold mb-4 text-center" }, P = { class: "grid grid-cols-8 gap-0.5 p-4 bg-green-800 rounded-lg border-2 border-green-900" }, W = ["onClick", "onMouseenter", "onMouseleave"], E = {
|
|
8
|
+
key: 1,
|
|
9
|
+
class: "w-10 h-10 flex items-center justify-center text-gray-300 text-sm font-bold"
|
|
10
|
+
}, V = /* @__PURE__ */ M({
|
|
11
|
+
__name: "View",
|
|
12
|
+
props: {
|
|
13
|
+
selectedResult: {},
|
|
14
|
+
sendTextMessage: { type: Function }
|
|
15
|
+
},
|
|
16
|
+
setup(u) {
|
|
17
|
+
const m = u, e = x(null), a = x(null);
|
|
18
|
+
L(
|
|
19
|
+
() => m.selectedResult,
|
|
20
|
+
(t) => {
|
|
21
|
+
t?.toolName === "playOthello" && t.jsonData && (e.value = t.jsonData);
|
|
22
|
+
},
|
|
23
|
+
{ immediate: !0 }
|
|
24
|
+
);
|
|
25
|
+
const w = g(() => {
|
|
26
|
+
if (!e.value?.playerNames) return "";
|
|
27
|
+
const t = e.value.playerNames[e.value.currentSide];
|
|
28
|
+
return t.charAt(0).toUpperCase() + t.slice(1);
|
|
29
|
+
}), y = g(() => e.value ? e.value.currentSide === "B" ? "Black" : "White" : ""), c = g(() => e.value?.playerNames && e.value.playerNames[e.value.currentSide] === "computer"), d = g(() => {
|
|
30
|
+
if (!e.value?.board) return [];
|
|
31
|
+
const t = [];
|
|
32
|
+
for (let r = 0; r < 8; r++)
|
|
33
|
+
for (let s = 0; s < 8; s++) {
|
|
34
|
+
const o = e.value.board[r][s], i = e.value.legalMoves?.some(
|
|
35
|
+
(k) => k.row === r && k.col === s
|
|
36
|
+
);
|
|
37
|
+
t.push({
|
|
38
|
+
row: r,
|
|
39
|
+
col: s,
|
|
40
|
+
piece: o !== "." ? o : null,
|
|
41
|
+
isLegalMove: i ?? !1,
|
|
42
|
+
label: i ? String.fromCharCode(65 + s) + (r + 1) : ""
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return t;
|
|
46
|
+
});
|
|
47
|
+
function _(t, r) {
|
|
48
|
+
const s = "w-12 h-12 flex items-center justify-center border border-green-900 bg-green-700", o = t.isLegalMove && !c.value && a.value === r ? "bg-green-600" : "", i = t.isLegalMove && !c.value && !e.value?.isTerminal ? "cursor-pointer hover:bg-green-600" : "cursor-default";
|
|
49
|
+
return `${s} ${o} ${i}`;
|
|
50
|
+
}
|
|
51
|
+
function j(t) {
|
|
52
|
+
return t === "B" ? "bg-black" : "bg-white";
|
|
53
|
+
}
|
|
54
|
+
function N(t) {
|
|
55
|
+
if (!e.value || e.value.isTerminal || c.value)
|
|
56
|
+
return;
|
|
57
|
+
const r = d.value[t];
|
|
58
|
+
if (!r.isLegalMove) return;
|
|
59
|
+
const s = String.fromCharCode(65 + r.col), o = r.row + 1;
|
|
60
|
+
m.sendTextMessage(
|
|
61
|
+
`I want to play at ${s}${o}, which is column=${r.col}, row=${r.row} `
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function C(t, r) {
|
|
65
|
+
!e.value || e.value.isTerminal || c.value || !d.value[t].isLegalMove || (a.value = r ? t : null);
|
|
66
|
+
}
|
|
67
|
+
return (t, r) => (n(), l("div", B, [
|
|
68
|
+
e.value ? (n(), l("div", O, [
|
|
69
|
+
v("div", S, " Current Turn: " + f(w.value) + " (" + f(y.value) + ") ", 1),
|
|
70
|
+
v("div", P, [
|
|
71
|
+
(n(!0), l(h, null, b(d.value, (s, o) => (n(), l("div", {
|
|
72
|
+
key: o,
|
|
73
|
+
class: $(_(s, o)),
|
|
74
|
+
onClick: (i) => N(o),
|
|
75
|
+
onMouseenter: (i) => C(o, !0),
|
|
76
|
+
onMouseleave: (i) => C(o, !1)
|
|
77
|
+
}, [
|
|
78
|
+
s.piece ? (n(), l("div", {
|
|
79
|
+
key: 0,
|
|
80
|
+
class: $([j(s.piece), "w-10 h-10 rounded-full border-2 border-gray-600"])
|
|
81
|
+
}, null, 2)) : s.isLegalMove && !c.value ? (n(), l("div", E, f(s.label), 1)) : p("", !0)
|
|
82
|
+
], 42, W))), 128))
|
|
83
|
+
])
|
|
84
|
+
])) : p("", !0)
|
|
85
|
+
]));
|
|
86
|
+
}
|
|
87
|
+
}), F = { class: "p-3 bg-green-50 rounded" }, A = {
|
|
88
|
+
key: 0,
|
|
89
|
+
class: "space-y-1"
|
|
90
|
+
}, I = { class: "flex justify-center" }, z = {
|
|
91
|
+
class: "inline-block",
|
|
92
|
+
style: { "background-color": "#2d5016", padding: "1px" }
|
|
93
|
+
}, G = {
|
|
94
|
+
class: "grid grid-cols-8",
|
|
95
|
+
style: { gap: "1px" }
|
|
96
|
+
}, R = {
|
|
97
|
+
key: 0,
|
|
98
|
+
class: "w-3 h-3 bg-black rounded-full"
|
|
99
|
+
}, U = {
|
|
100
|
+
key: 1,
|
|
101
|
+
class: "w-3 h-3 bg-white rounded-full"
|
|
102
|
+
}, H = { class: "text-xs text-center space-y-1" }, Y = {
|
|
103
|
+
key: 0,
|
|
104
|
+
class: "text-gray-600"
|
|
105
|
+
}, q = {
|
|
106
|
+
key: 1,
|
|
107
|
+
class: "font-medium"
|
|
108
|
+
}, J = /* @__PURE__ */ M({
|
|
109
|
+
__name: "Preview",
|
|
110
|
+
props: {
|
|
111
|
+
result: {}
|
|
112
|
+
},
|
|
113
|
+
setup(u) {
|
|
114
|
+
function m(a) {
|
|
115
|
+
return a.isTerminal ? a.winner === "draw" ? "Draw!" : a.winner === "B" ? "⚫ Black Wins!" : a.winner === "W" ? "⚪ White Wins!" : "Game Over" : "";
|
|
116
|
+
}
|
|
117
|
+
function e(a) {
|
|
118
|
+
return a.charAt(0).toUpperCase() + a.slice(1);
|
|
119
|
+
}
|
|
120
|
+
return (a, w) => (n(), l("div", F, [
|
|
121
|
+
u.result.jsonData ? (n(), l("div", A, [
|
|
122
|
+
v("div", I, [
|
|
123
|
+
v("div", z, [
|
|
124
|
+
v("div", G, [
|
|
125
|
+
(n(!0), l(h, null, b(u.result.jsonData.board, (y, c) => (n(), l(h, { key: c }, [
|
|
126
|
+
(n(!0), l(h, null, b(y, (d, _) => (n(), l("div", {
|
|
127
|
+
key: `${c}-${_}`,
|
|
128
|
+
class: "w-4 h-4 flex items-center justify-center",
|
|
129
|
+
style: { "background-color": "#3d6b20" }
|
|
130
|
+
}, [
|
|
131
|
+
d === "B" ? (n(), l("div", R)) : d === "W" ? (n(), l("div", U)) : p("", !0)
|
|
132
|
+
]))), 128))
|
|
133
|
+
], 64))), 128))
|
|
134
|
+
])
|
|
135
|
+
])
|
|
136
|
+
]),
|
|
137
|
+
v("div", H, [
|
|
138
|
+
u.result.jsonData.isTerminal ? (n(), l("div", q, f(m(u.result.jsonData)), 1)) : (n(), l("div", Y, f(u.result.jsonData.currentSide === "B" ? "⚫" : "⚪") + " " + f(e(
|
|
139
|
+
u.result.jsonData.playerNames[u.result.jsonData.currentSide]
|
|
140
|
+
)) + " to play ", 1))
|
|
141
|
+
])
|
|
142
|
+
])) : p("", !0)
|
|
143
|
+
]));
|
|
144
|
+
}
|
|
145
|
+
}), K = {
|
|
146
|
+
...D,
|
|
147
|
+
viewComponent: V,
|
|
148
|
+
previewComponent: J,
|
|
149
|
+
samples: T
|
|
150
|
+
}, Z = { plugin: K };
|
|
151
|
+
export {
|
|
152
|
+
J as Preview,
|
|
153
|
+
re as SYSTEM_PROMPT,
|
|
154
|
+
se as TOOL_DEFINITION,
|
|
155
|
+
le as TOOL_NAME,
|
|
156
|
+
V as View,
|
|
157
|
+
Z as default,
|
|
158
|
+
ne as executeOthello,
|
|
159
|
+
oe as playOthello,
|
|
160
|
+
K as plugin,
|
|
161
|
+
D as pluginCore,
|
|
162
|
+
T as samples
|
|
163
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gui-chat-plugin/othello",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Othello/Reversi game plugin for GUI Chat",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./core": {
|
|
16
|
+
"types": "./dist/core/index.d.ts",
|
|
17
|
+
"import": "./dist/core.js",
|
|
18
|
+
"require": "./dist/core.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./vue": {
|
|
21
|
+
"types": "./dist/vue/index.d.ts",
|
|
22
|
+
"import": "./dist/vue.js",
|
|
23
|
+
"require": "./dist/vue.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./style.css": "./dist/style.css"
|
|
26
|
+
},
|
|
27
|
+
"files": ["dist"],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"dev": "vite",
|
|
30
|
+
"build": "vite build && vue-tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
31
|
+
"typecheck": "vue-tsc --noEmit",
|
|
32
|
+
"lint": "eslint src demo"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"vue": "^3.5.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"gui-chat-protocol": "^0.0.1"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
42
|
+
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
43
|
+
"@typescript-eslint/parser": "^8.53.0",
|
|
44
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
45
|
+
"eslint": "^9.39.2",
|
|
46
|
+
"eslint-plugin-vue": "^10.6.2",
|
|
47
|
+
"globals": "^17.0.0",
|
|
48
|
+
"tailwindcss": "^4.1.18",
|
|
49
|
+
"typescript": "~5.9.3",
|
|
50
|
+
"vite": "^7.3.1",
|
|
51
|
+
"vue": "^3.5.26",
|
|
52
|
+
"vue-eslint-parser": "^10.2.0",
|
|
53
|
+
"vue-tsc": "^3.2.2"
|
|
54
|
+
},
|
|
55
|
+
"keywords": ["guichat", "plugin", "othello", "reversi", "game"],
|
|
56
|
+
"license": "MIT"
|
|
57
|
+
}
|