Persist code
This commit is contained in:
@@ -50,13 +50,24 @@ interface EditorProps {
|
|||||||
fontSize?: number;
|
fontSize?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STORAGE_KEY = "minimax-editor-content";
|
||||||
|
|
||||||
export const Editor = forwardRef<any, EditorProps>(function Editor(
|
export const Editor = forwardRef<any, EditorProps>(function Editor(
|
||||||
{ initialValue = "", onChange, onReady, fontSize = 14 },
|
{ initialValue = "", onChange, onReady, fontSize = 14 },
|
||||||
ref
|
ref
|
||||||
) {
|
) {
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const editorRef = useRef<any>(null);
|
const editorRef = useRef<any>(null);
|
||||||
const [content, setContent] = useState(initialValue);
|
|
||||||
|
const getInitialContent = () => {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const saved = localStorage.getItem(STORAGE_KEY);
|
||||||
|
return saved || initialValue;
|
||||||
|
}
|
||||||
|
return initialValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [content, setContent] = useState(getInitialContent);
|
||||||
|
|
||||||
useImperativeHandle(ref, () => editorRef.current);
|
useImperativeHandle(ref, () => editorRef.current);
|
||||||
|
|
||||||
@@ -100,6 +111,11 @@ export const Editor = forwardRef<any, EditorProps>(function Editor(
|
|||||||
editor.on("change", (instance: any, changes: any) => {
|
editor.on("change", (instance: any, changes: any) => {
|
||||||
const newContent = instance.getValue();
|
const newContent = instance.getValue();
|
||||||
setContent(newContent);
|
setContent(newContent);
|
||||||
|
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
localStorage.setItem(STORAGE_KEY, newContent);
|
||||||
|
}
|
||||||
|
|
||||||
onChange?.(instance, changes);
|
onChange?.(instance, changes);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -129,7 +145,7 @@ export const Editor = forwardRef<any, EditorProps>(function Editor(
|
|||||||
<div className={styles.editorContainer}>
|
<div className={styles.editorContainer}>
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
defaultValue={initialValue}
|
defaultValue={content}
|
||||||
placeholder="Code goes here"
|
placeholder="Code goes here"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -85,11 +85,14 @@ export default function Playground() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
if (resultRef.current) {
|
if (resultRef.current) {
|
||||||
resultRef.current.value += `\nScript exited with error:\n${ex}\n`;
|
resultRef.current.value += `\nScript exited with error:\n${ex}\n`;
|
||||||
}
|
}
|
||||||
terminalRef.current?.write(
|
terminalRef.current?.write(
|
||||||
"\r\n\x1B[1;31mScript exited with error:\x1B[0m\n\r" + String(ex).replace("\n", "\n\r") + "\r\n"
|
"\r\n\x1B[1;31mScript exited with error:\x1B[0m\n\r" +
|
||||||
|
String(ex).replace("\n", "\n\r") +
|
||||||
|
"\r\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,11 +133,14 @@ export default function Playground() {
|
|||||||
bulkRounds
|
bulkRounds
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (resultRef.current) {
|
console.error(ex);
|
||||||
|
if (resultRef.current) {
|
||||||
resultRef.current.value += `\nScript exited with error:\n${ex}\n`;
|
resultRef.current.value += `\nScript exited with error:\n${ex}\n`;
|
||||||
}
|
}
|
||||||
terminalRef.current?.write(
|
terminalRef.current?.write(
|
||||||
"\r\n\x1B[1;31mScript exited with error:\x1B[0m\n\r" + String(ex).replace("\n", "\n\r") + "\r\n"
|
"\r\n\x1B[1;31mScript exited with error:\x1B[0m\n\r" +
|
||||||
|
String(ex).replace("\n", "\n\r") +
|
||||||
|
"\r\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,100 +263,243 @@ export default function Playground() {
|
|||||||
<h2>Game Rules</h2>
|
<h2>Game Rules</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This game is played in two rounds,
|
This game is played in two rounds, on an empty eleven-space
|
||||||
on an empty eleven-space board.
|
board. The first round is played on {"Red's"} board, the
|
||||||
The first round is played on {"Red's"} board, the second is played on {"Blue's"}.
|
second is played on {"Blue's"}.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
On {"Red's"} board, {"Red's"} goal is to maximize the value of the expression.
|
On {"Red's"} board, {"Red's"} goal is to maximize the value
|
||||||
{" Blue's"} goal is to minimize it.
|
of the expression.
|
||||||
|
{" Blue's"} goal is to minimize it. Players take turns
|
||||||
Players take turns placing the fourteen symbols <code>0123456789+-×÷</code>
|
placing the fourteen symbols <code>0123456789+-×÷</code>
|
||||||
on the board, with the maximizing player taking the first move.
|
on the board, with the maximizing player taking the first
|
||||||
|
move.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
A {"board's"} syntax must always be valid, and
|
A {"board's"} syntax must always be valid, and the following
|
||||||
the following rules are enforced:
|
rules are enforced:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>Each symbol may only be used once</li>
|
<li>Each symbol may only be used once</li>
|
||||||
<li>The binary operators <code>+-×÷</code> may not be next to one another, and may not be at the end slots.</li>
|
<li>
|
||||||
<li>The unary operator <code>-</code> (negative) must have a number as an argument. Therefore, it cannot be left of an operator (like <code>-×</code>), and it may not be in the rightmost slot.</li>
|
The binary operators <code>+-×÷</code> may not be next
|
||||||
<li>Unary <code>+</code> may not be used.</li>
|
to one another, and may not be at the end slots.
|
||||||
<li> <code>0</code> may not follow <code>÷</code>.
|
</li>
|
||||||
This prevents most cases of zero-division, but {"isn't perfect"}.
|
<li>
|
||||||
<code>÷-0</code> will result in an invalid board (causing a draw),
|
The unary operator <code>-</code> (negative) must have a
|
||||||
and <code>÷0_+</code> is forbidden despite being valid syntax once the empty slot is filled.
|
number as an argument. Therefore, it cannot be left of
|
||||||
This is done to simplyify game logic, and might be improved later.
|
an operator (like <code>-×</code>), and it may not be in
|
||||||
|
the rightmost slot.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Unary <code>+</code> may not be used.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{" "}
|
||||||
|
<code>0</code> may not follow <code>÷</code>. This
|
||||||
|
prevents most cases of zero-division, but{" "}
|
||||||
|
{"isn't perfect"}.<code>÷-0</code> will result in an
|
||||||
|
invalid board (causing a draw), and <code>÷0_+</code> is
|
||||||
|
forbidden despite being valid syntax once the empty slot
|
||||||
|
is filled. This is done to simplyify game logic, and
|
||||||
|
might be improved later.
|
||||||
</li>
|
</li>
|
||||||
<li>Division by zero results in a draw.</li>
|
<li>Division by zero results in a draw.</li>
|
||||||
<li>An incomplete board with no valid moves results in a draw.</li>
|
<li>
|
||||||
|
An incomplete board with no valid moves results in a
|
||||||
|
draw.
|
||||||
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<h2>How to Play</h2>
|
<h2>How to Play</h2>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Click <strong>Run</strong> to start a single game. Play against your agent in the terminal. Use your arrow keys (up, down, left, right) to select a symbol. Use enter or space to make a move.</li>
|
<li>
|
||||||
<li>Click <strong>Bulk Run</strong> to collect statistics from a many games.</li>
|
Click <strong>Run</strong> to start a single game. Play
|
||||||
|
against your agent in the terminal. Use your arrow keys
|
||||||
|
(up, down, left, right) to select a symbol. Use enter or
|
||||||
|
space to make a move.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Click <strong>Bulk Run</strong> to collect statistics
|
||||||
|
from a many games.
|
||||||
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
|
||||||
<h2>Overview</h2>
|
<h2>Overview</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>step_min()</code> is called once per turn with the {"board's"} current state. This function must return an <code>Action</code> that aims to minimize the total value of the board.</li>
|
<li>
|
||||||
<li><code>step_max()</code> is just like <code>step_min</code>, but should aim to maximize the value of the board. </li>
|
<code>step_min()</code> is called once per turn with the{" "}
|
||||||
<li>Agent code may not be edited between games. Start a new game to use new code.</li>
|
{"board's"} current state. This function must return an{" "}
|
||||||
<li>If your agent takes more than 5 seconds to compute a move, the script will exit with an error.</li>
|
<code>Action</code> that aims to minimize the total
|
||||||
|
value of the board.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>step_max()</code> is just like{" "}
|
||||||
|
<code>step_min</code>, but should aim to maximize the
|
||||||
|
value of the board.{" "}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Agent code may not be edited between games. Start a new
|
||||||
|
game to use new code.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
If your agent takes more than 5 seconds to compute a
|
||||||
|
move, the script will exit with an error.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Rhai basics</h2>
|
<h2>Rhai basics</h2>
|
||||||
<p>
|
<p>
|
||||||
Agents are written in <a href="https://rhai.rs">Rhai</a>, a wonderful embedded scripting language powered by Rust.
|
Agents are written in <a href="https://rhai.rs">Rhai</a>, a
|
||||||
Basic language features are outlined below.
|
wonderful embedded scripting language powered by Rust. Basic
|
||||||
|
language features are outlined below.
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>All statements must be followed by a <code>;</code></li>
|
<li>
|
||||||
<li>Use <code>return</code> to return a value from a function.</li>
|
All statements must be followed by a <code>;</code>
|
||||||
<li><code>print(anything)</code> - Prints to the output panel. Prefer this over <code>debug</code>.</li>
|
</li>
|
||||||
<li><code>debug(anything)</code> - Prints to the output panel. Includes extra debug info.</li>
|
<li>
|
||||||
<li><code>()</code> is the {"\"none\""} type, returned by some methods above.</li>
|
Use <code>return</code> to return a value from a
|
||||||
<li><code>for i in 0..5 {"{}"}</code> will iterate five times, with <code>i = 0, 1, 2, 3, 4</code></li>
|
function.
|
||||||
<li><code>for i in 0..=5 {"{}"}</code> will iterate six times, with <code>i = 0, 1, 2, 3, 4, 5</code></li>
|
</li>
|
||||||
<li><code>let a = [];</code> initializes an empty array.</li>
|
<li>
|
||||||
<li><code>a.push(value)</code> adds a value to the end of an array</li>
|
<code>print(anything)</code> - Prints to the output
|
||||||
<li><code>a.pop()</code> removes a value from the end of an array and returns it</li>
|
panel. Prefer this over <code>debug</code>.
|
||||||
<li><code>a[0]</code> returns the first item of an array</li>
|
</li>
|
||||||
<li><code>a[1]</code> returns the second item of an array</li>
|
<li>
|
||||||
<li>Refer to <a href="https://rhai.rs/book/language/values-and-types.html">the Rhai book</a> for more details.</li>
|
<code>debug(anything)</code> - Prints to the output
|
||||||
|
panel. Includes extra debug info.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>()</code> is the {'"none"'} type, returned by some
|
||||||
|
methods above.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>for i in 0..5 {"{}"}</code> will iterate five
|
||||||
|
times, with <code>i = 0, 1, 2, 3, 4</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>for i in 0..=5 {"{}"}</code> will iterate six
|
||||||
|
times, with <code>i = 0, 1, 2, 3, 4, 5</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>let a = [];</code> initializes an empty array.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>a.push(value)</code> adds a value to the end of an
|
||||||
|
array
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>a.pop()</code> removes a value from the end of an
|
||||||
|
array and returns it
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>a[0]</code> returns the first item of an array
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>a[1]</code> returns the second item of an array
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Refer to{" "}
|
||||||
|
<a href="https://rhai.rs/book/language/values-and-types.html">
|
||||||
|
the Rhai book
|
||||||
|
</a>{" "}
|
||||||
|
for more details.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Notable Functions</h2>
|
<h2>Notable Functions</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>Action(symbol, position)</code> - Creates a new action that places <code>symbol</code> at <code>position</code>. Valid symbols are <code>01234567890+-/*</code>. Both <code>0</code> and <code>{"\"0\""}</code> are valid symbols.</li>
|
<li>
|
||||||
<li><code>board.can_play(action)</code> - Checks if an action is valid. Returns a boolean.</li>
|
<code>Action(symbol, position)</code> - Creates a new
|
||||||
<li><code>board.size()</code> - Return the total number of spots on this board.</li>
|
action that places <code>symbol</code> at{" "}
|
||||||
<li><code>board.free_spots()</code> - Count the number of free spots on the board.</li>
|
<code>position</code>. Valid symbols are{" "}
|
||||||
<li><code>board.play(action)</code> - Apply the given action on this board. This mutates the <code>board</code>, but does NOT make the move in the game. The only way to commit to an action is to return it from <code>step_min</code> or <code>step_max</code>.
|
<code>01234567890+-/*</code>. Both <code>0</code> and{" "}
|
||||||
This method lets you compute potential values of a board when used with <code>board.evaluate()</code>.
|
<code>{'"0"'}</code> are valid symbols.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>board.can_play(action)</code> - Checks if an
|
||||||
|
action is valid. Returns a boolean.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>board.size()</code> - Return the total number of
|
||||||
|
spots on this board.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>board.free_spots()</code> - Count the number of
|
||||||
|
free spots on the board.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>board.play(action)</code> - Apply the given action
|
||||||
|
on this board. This mutates the <code>board</code>, but
|
||||||
|
does NOT make the move in the game. The only way to
|
||||||
|
commit to an action is to return it from{" "}
|
||||||
|
<code>step_min</code> or <code>step_max</code>. This
|
||||||
|
method lets you compute potential values of a board when
|
||||||
|
used with <code>board.evaluate()</code>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>board.ith_free_slot(idx)</code> - Returns the
|
||||||
|
index of the <code>n</code>th free slot on this board.
|
||||||
|
Returns <code>-1</code> if no such slot exists.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>board.contains(symbol)</code> - Checks if this
|
||||||
|
board contains the given symbol. Returns a boolean.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>board.evaluate()</code> - Return the value of a
|
||||||
|
board if it can be computed. Returns <code>()</code>{" "}
|
||||||
|
otherwise.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>board.free_spots_idx(action)</code> - Checks if an
|
||||||
|
action is valid. Returns a boolean.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>for i in board {"{ ... }"}</code> - Iterate over
|
||||||
|
all slots on this board. Items are returned as strings,
|
||||||
|
empty slots are the empty string (<code>{'""'}</code>)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>is_op(symbol)</code> - Returns <code>true</code>{" "}
|
||||||
|
if <code>symbol</code> is one of <code>+-*/</code>
|
||||||
</li>
|
</li>
|
||||||
<li><code>board.ith_free_slot(idx)</code> - Returns the index of the <code>n</code>th free slot on this board. Returns <code>-1</code> if no such slot exists.</li>
|
|
||||||
<li><code>board.contains(symbol)</code> - Checks if this board contains the given symbol. Returns a boolean.</li>
|
|
||||||
<li><code>board.evaluate()</code> - Return the value of a board if it can be computed. Returns <code>()</code> otherwise.</li>
|
|
||||||
<li><code>board.free_spots_idx(action)</code> - Checks if an action is valid. Returns a boolean.</li>
|
|
||||||
<li><code>for i in board {"{ ... }"}</code> - Iterate over all slots on this board. Items are returned as strings, empty slots are the empty string (<code>{"\"\""}</code>)</li>
|
|
||||||
<li><code>is_op(symbol)</code> - Returns <code>true</code> if <code>symbol</code> is one of <code>+-*/</code></li>
|
|
||||||
|
|
||||||
<li><code>rand_symb()</code> - Returns a random symbol (number or operation)</li>
|
<li>
|
||||||
<li><code>rand_op()</code> - Returns a random operator symbol (one of <code>+-*/</code>)</li>
|
<code>rand_symb()</code> - Returns a random symbol
|
||||||
<li><code>rand_action()</code> - Returns a random <code>Action</code></li>
|
(number or operation)
|
||||||
<li><code>rand_int(min, max)</code> - Returns a random integer between min and max, including both endpoints.</li>
|
</li>
|
||||||
<li><code>rand_bool(probability)</code> - Return <code>true</code> with the given probability. Otherwise return <code>false</code>.</li>
|
<li>
|
||||||
<li><code>rand_shuffle(array)</code> - Shuffle the given array</li>
|
<code>rand_op()</code> - Returns a random operator
|
||||||
<li><code>for p in permutations(array, 5) {"{}"}</code> - Iterate over all permutations of 5 elements of the given array.</li>
|
symbol (one of <code>+-*/</code>)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>rand_action()</code> - Returns a random{" "}
|
||||||
|
<code>Action</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>rand_int(min, max)</code> - Returns a random
|
||||||
|
integer between min and max, including both endpoints.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>rand_bool(probability)</code> - Return{" "}
|
||||||
|
<code>true</code> with the given probability. Otherwise
|
||||||
|
return <code>false</code>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>rand_shuffle(array)</code> - Shuffle the given
|
||||||
|
array
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>for p in permutations(array, 5) {"{}"}</code> -
|
||||||
|
Iterate over all permutations of 5 elements of the given
|
||||||
|
array.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</SidePanel>
|
</SidePanel>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user