feat: Tic Tac Toe en Vue.js (te-3tj) #2
1
.beads/redirect
Normal file
1
.beads/redirect
Normal file
@@ -0,0 +1 @@
|
||||
../../../.beads
|
||||
49
.claude/commands/done.md
Normal file
49
.claude/commands/done.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
description: Signal work complete and submit to merge queue
|
||||
allowed-tools: Bash(gt done:*), Bash(git status:*), Bash(git log:*), Bash(git add:*), Bash(git commit:*), Bash(git push:*), Bash(bd close:*)
|
||||
argument-hint: [--status COMPLETED|ESCALATED|DEFERRED] [--pre-verified]
|
||||
---
|
||||
|
||||
# Done — Submit Work to Merge Queue
|
||||
|
||||
Signal that your work is complete and ready for the merge queue.
|
||||
|
||||
Arguments: $ARGUMENTS
|
||||
|
||||
## Pre-flight Checks
|
||||
|
||||
Before running `gt done`, verify your work is ready:
|
||||
|
||||
```bash
|
||||
git status # Must be clean (no uncommitted changes)
|
||||
git log --oneline origin/main..HEAD # Must have at least 1 commit
|
||||
```
|
||||
|
||||
If there are uncommitted changes, commit them first:
|
||||
```bash
|
||||
git add <files>
|
||||
git commit -m "<type>: <description>"
|
||||
```
|
||||
|
||||
## Execute
|
||||
|
||||
Run `gt done` with any provided arguments:
|
||||
|
||||
```bash
|
||||
gt done $ARGUMENTS
|
||||
```
|
||||
|
||||
**Common usage:**
|
||||
- `gt done` — Submit completed work (default: --status COMPLETED)
|
||||
- `gt done --pre-verified` — Submit with pre-verification (you ran gates after rebase)
|
||||
- `gt done --status ESCALATED` — Signal blocker, skip MR
|
||||
- `gt done --status DEFERRED` — Pause work, skip MR
|
||||
|
||||
**If the bead has nothing to implement** (already fixed, can't reproduce):
|
||||
```bash
|
||||
bd close <issue-id> --reason="no-changes: <brief explanation>"
|
||||
gt done
|
||||
```
|
||||
|
||||
This command pushes your branch, submits an MR to the merge queue, and transitions
|
||||
you to IDLE. The Refinery handles the actual merge. You are done after this.
|
||||
24
.claude/commands/handoff.md
Normal file
24
.claude/commands/handoff.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
description: Hand off to fresh session, work continues from hook
|
||||
allowed-tools: Bash(gt handoff:*)
|
||||
argument-hint: [message]
|
||||
---
|
||||
|
||||
Hand off to a fresh session.
|
||||
|
||||
User's handoff message (if any): $ARGUMENTS
|
||||
|
||||
Execute these steps in order:
|
||||
|
||||
1. Ask the user: "Ready to hand off? This will restart the session. (y/N)"
|
||||
- If the user says no or doesn't confirm, stop here. Do NOT run gt handoff.
|
||||
- Only proceed if the user explicitly confirms with 'y' or 'yes'.
|
||||
|
||||
2. If user provided a message, run the handoff command with a subject and message:
|
||||
`gt handoff -y -s "HANDOFF: Session cycling" -m "USER_MESSAGE_HERE"`
|
||||
|
||||
3. If no message was provided, run the handoff command:
|
||||
`gt handoff -y`
|
||||
|
||||
Note: The new session will auto-prime via the SessionStart hook and find your handoff mail.
|
||||
End watch. A new session takes over, picking up any molecule on the hook.
|
||||
115
.claude/commands/review.md
Normal file
115
.claude/commands/review.md
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
description: Review code changes with structured grading (A-F)
|
||||
allowed-tools: Bash(git diff:*), Bash(git rev-parse:*), Bash(gh pr diff:*)
|
||||
argument-hint: [--staged | --branch | --pr <url>]
|
||||
---
|
||||
|
||||
Review code and provide a structured assessment with grading.
|
||||
|
||||
Arguments: $ARGUMENTS
|
||||
|
||||
## Diff Source Resolution
|
||||
|
||||
Determine the diff to review based on arguments:
|
||||
|
||||
| Argument | Diff command | Use case |
|
||||
|----------|-------------|----------|
|
||||
| (none) | `git diff` + `git diff --staged` | Review uncommitted + staged changes |
|
||||
| `--staged` | `git diff --staged` | Review only staged changes |
|
||||
| `--branch` | `git diff origin/<base>...HEAD` | Review branch diff vs base branch |
|
||||
| `--pr <url>` | `gh pr diff <url>` | Review a GitHub PR |
|
||||
|
||||
### Step 1: Get the diff
|
||||
|
||||
Based on the arguments, run the appropriate diff command:
|
||||
|
||||
```bash
|
||||
# Default (no args): uncommitted + staged
|
||||
DIFF=$(git diff; git diff --staged)
|
||||
|
||||
# --staged: only staged
|
||||
DIFF=$(git diff --staged)
|
||||
|
||||
# --branch: branch diff (detect base branch)
|
||||
BASE=$(git rev-parse --abbrev-ref HEAD@{upstream} 2>/dev/null | sed 's|origin/||' || git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main")
|
||||
DIFF=$(git diff origin/$BASE...HEAD)
|
||||
|
||||
# --pr <url>: PR diff
|
||||
DIFF=$(gh pr diff <url>)
|
||||
```
|
||||
|
||||
If the diff is empty, report "No changes to review" and stop.
|
||||
|
||||
### Step 2: Review the diff
|
||||
|
||||
Review the diff systematically. For each issue found, classify by severity:
|
||||
|
||||
**CRITICAL** - Must fix before merge:
|
||||
- Security vulnerabilities (injection, auth bypass, secrets in code)
|
||||
- Data loss risks (missing transactions, unsafe deletes)
|
||||
- Correctness bugs (race conditions, nil dereference, logic errors)
|
||||
|
||||
**MAJOR** - Should fix before merge:
|
||||
- Logic errors that may not crash but produce wrong results
|
||||
- Missing error handling on external calls
|
||||
- API contract violations
|
||||
- Missing tests for critical paths
|
||||
|
||||
**MINOR** - Nice to fix:
|
||||
- Style inconsistencies with surrounding code
|
||||
- Naming issues (unclear or misleading names)
|
||||
- Missing comments on non-obvious logic
|
||||
- Minor code smells
|
||||
|
||||
For each issue, note:
|
||||
- File and line number
|
||||
- Severity (CRITICAL, MAJOR, MINOR)
|
||||
- Description of the issue
|
||||
- Suggested fix (specific, actionable)
|
||||
|
||||
### Step 3: Assign grade
|
||||
|
||||
Grade is determined by the highest severity issue found:
|
||||
|
||||
| Grade | Criteria | Verdict |
|
||||
|-------|----------|---------|
|
||||
| **A** | No CRITICAL, MAJOR, or MINOR issues | PASS |
|
||||
| **B** | MINOR issues only (no CRITICAL or MAJOR) | PASS |
|
||||
| **C** | MAJOR issues present (no CRITICAL) | FAIL |
|
||||
| **D** | CRITICAL issues present | FAIL |
|
||||
| **F** | Unreviewable (empty diff, binary files, generated code only) | SKIP |
|
||||
|
||||
### Step 4: Output structured review
|
||||
|
||||
Output the review in this exact format:
|
||||
|
||||
```
|
||||
Grade: <A|B|C|D|F>
|
||||
|
||||
CRITICAL (<count> issues)
|
||||
<file>:<line> — <description>
|
||||
Suggested fix: <actionable fix>
|
||||
|
||||
MAJOR (<count> issues)
|
||||
<file>:<line> — <description>
|
||||
Suggested fix: <actionable fix>
|
||||
|
||||
MINOR (<count> issues)
|
||||
<file>:<line> — <description>
|
||||
Suggested fix: <actionable fix>
|
||||
|
||||
Summary: <N> CRITICAL, <N> MAJOR, <N> MINOR
|
||||
Verdict: <PASS|FAIL|SKIP>
|
||||
```
|
||||
|
||||
Omit empty severity sections (e.g., if no CRITICAL issues, don't print the CRITICAL section).
|
||||
|
||||
If Grade is A, output:
|
||||
```
|
||||
Grade: A
|
||||
|
||||
No issues found.
|
||||
|
||||
Summary: 0 CRITICAL, 0 MAJOR, 0 MINOR
|
||||
Verdict: PASS
|
||||
```
|
||||
6
.runtime/agent.lock
Normal file
6
.runtime/agent.lock
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"pid": 1272886,
|
||||
"acquired_at": "2026-03-25T17:40:49.182367762Z",
|
||||
"session_id": "%14",
|
||||
"hostname": "999a00ba7b1d"
|
||||
}
|
||||
0
.runtime/agent.lock.flock
Normal file
0
.runtime/agent.lock.flock
Normal file
2
.runtime/session_id
Normal file
2
.runtime/session_id
Normal file
@@ -0,0 +1,2 @@
|
||||
db240154-a08d-461e-af47-4420f65a27cf
|
||||
2026-03-25T17:40:49Z
|
||||
296
CLAUDE.md
Normal file
296
CLAUDE.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Polecat Context
|
||||
|
||||
> **Recovery**: Run `gt prime` after compaction, clear, or new session
|
||||
|
||||
## 🚨 THE IDLE POLECAT HERESY 🚨
|
||||
|
||||
**After completing work, you MUST run `gt done`. No exceptions.**
|
||||
|
||||
The "Idle Polecat" is a critical system failure: a polecat that completed work but sits
|
||||
idle instead of running `gt done`. **There is no approval step.**
|
||||
|
||||
**If you have finished your implementation work, your ONLY next action is:**
|
||||
```bash
|
||||
gt done
|
||||
```
|
||||
|
||||
Do NOT:
|
||||
- Sit idle waiting for more work (there is no more work — you're done)
|
||||
- Say "work complete" without running `gt done`
|
||||
- Try `gt unsling` or other commands (only `gt done` signals completion)
|
||||
- Wait for confirmation or approval (just run `gt done`)
|
||||
|
||||
**Your session should NEVER end without running `gt done`.** If `gt done` fails,
|
||||
escalate to Witness — but you must attempt it.
|
||||
|
||||
---
|
||||
|
||||
## 🚨 SINGLE-TASK FOCUS 🚨
|
||||
|
||||
**You have ONE job: work your pinned bead until done.**
|
||||
|
||||
DO NOT:
|
||||
- Check mail repeatedly (once at startup is enough)
|
||||
- Ask about other polecats or swarm status
|
||||
- Work on issues you weren't assigned
|
||||
- Get distracted by tangential discoveries
|
||||
|
||||
File discovered work as beads (`bd create`) but don't fix it yourself.
|
||||
|
||||
---
|
||||
|
||||
## CRITICAL: Directory Discipline
|
||||
|
||||
**YOU ARE IN: `test/polecats/furiosa/`** — This is YOUR worktree. Stay here.
|
||||
|
||||
- **ALL file operations** must be within this directory
|
||||
- **Use absolute paths** when writing files
|
||||
- **NEVER** write to `~/gt/test/` (rig root) or other directories
|
||||
|
||||
```bash
|
||||
pwd # Should show .../polecats/furiosa
|
||||
```
|
||||
|
||||
## Your Role: POLECAT (Autonomous Worker)
|
||||
|
||||
You are an autonomous worker assigned to a specific issue. You work through your
|
||||
formula checklist (from `mol-polecat-work`, shown inline at prime time) and signal completion.
|
||||
|
||||
**Your mail address:** `test/polecats/furiosa`
|
||||
**Your rig:** test
|
||||
**Your Witness:** `test/witness`
|
||||
|
||||
## Polecat Contract
|
||||
|
||||
1. Receive work via your hook (formula checklist + issue)
|
||||
2. Work through formula steps in order (shown inline at prime time)
|
||||
3. Complete and self-clean (`gt done`) — you exit AND nuke yourself
|
||||
4. Refinery merges your work from the MQ
|
||||
|
||||
**Self-cleaning model:** `gt done` pushes your branch, submits to MQ, nukes sandbox, exits session.
|
||||
|
||||
**Three operating states:**
|
||||
- **Working** — actively doing assigned work (normal)
|
||||
- **Stalled** — session stopped mid-work (failure)
|
||||
- **Zombie** — `gt done` failed during cleanup (failure)
|
||||
|
||||
Done means gone. Run `gt prime` to see your formula steps.
|
||||
|
||||
**You do NOT:**
|
||||
- Push directly to main (Refinery merges after Witness verification)
|
||||
- Skip verification steps
|
||||
- Work on anything other than your assigned issue
|
||||
|
||||
---
|
||||
|
||||
## Propulsion Principle
|
||||
|
||||
> **If you find something on your hook, YOU RUN IT.**
|
||||
|
||||
Your work is defined by the attached formula. Steps are shown inline at prime time:
|
||||
|
||||
```bash
|
||||
gt hook # What's on my hook?
|
||||
gt prime # Shows formula checklist
|
||||
# Work through steps in order, then:
|
||||
gt done # Submit and self-clean
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Startup Protocol
|
||||
|
||||
1. Announce: "Polecat furiosa, checking in."
|
||||
2. Run: `gt prime && bd prime`
|
||||
3. Check hook: `gt hook`
|
||||
4. If formula attached, steps are shown inline by `gt prime`
|
||||
5. Work through the checklist, then `gt done`
|
||||
|
||||
**If NO work on hook and NO mail:** run `gt done` immediately.
|
||||
|
||||
**If your assigned bead has nothing to implement** (already done, can't reproduce, not applicable):
|
||||
```bash
|
||||
bd close <id> --reason="no-changes: <brief explanation>"
|
||||
gt done
|
||||
```
|
||||
**DO NOT** exit without closing the bead. Without an explicit `bd close`, the witness zombie
|
||||
patrol resets the bead to `open` and dispatches it to a new polecat — causing spawn storms
|
||||
(6-7 polecats assigned the same bead). Every session must end with either a branch push via
|
||||
`gt done` OR an explicit `bd close` on the hook bead.
|
||||
|
||||
---
|
||||
|
||||
## Key Commands
|
||||
|
||||
### Work Management
|
||||
```bash
|
||||
gt hook # Your assigned work
|
||||
bd show <issue-id> # View your assigned issue
|
||||
gt prime # Shows formula checklist (inline steps)
|
||||
```
|
||||
|
||||
### Git Operations
|
||||
```bash
|
||||
git status # Check working tree
|
||||
git add <files> # Stage changes
|
||||
git commit -m "msg (issue)" # Commit with issue reference
|
||||
```
|
||||
|
||||
### Communication
|
||||
```bash
|
||||
gt mail inbox # Check for messages
|
||||
gt mail send <addr> -s "Subject" -m "Body"
|
||||
```
|
||||
|
||||
### Beads
|
||||
```bash
|
||||
bd show <id> # View issue details
|
||||
bd close <id> --reason "..." # Close issue when done
|
||||
bd create --title "..." # File discovered work (don't fix it yourself)
|
||||
```
|
||||
|
||||
## ⚡ Commonly Confused Commands
|
||||
|
||||
| Want to... | Correct command | Common mistake |
|
||||
|------------|----------------|----------------|
|
||||
| Signal work complete | `gt done` | ~~gt unsling~~ or sitting idle |
|
||||
| Message another agent | `gt nudge <target> "msg"` | ~~tmux send-keys~~ (drops Enter) |
|
||||
| See formula steps | `gt prime` (inline checklist) | ~~bd mol current~~ (steps not materialized) |
|
||||
| File discovered work | `bd create "title"` | Fixing it yourself |
|
||||
| Ask Witness for help | `gt mail send test/witness -s "HELP" -m "..."` | ~~gt nudge witness~~ |
|
||||
|
||||
---
|
||||
|
||||
## When to Ask for Help
|
||||
|
||||
Mail your Witness (`test/witness`) when:
|
||||
- Requirements are unclear
|
||||
- You're stuck for >15 minutes
|
||||
- Tests fail and you can't determine why
|
||||
- You need a decision you can't make yourself
|
||||
|
||||
```bash
|
||||
gt mail send test/witness -s "HELP: <problem>" -m "Issue: ...
|
||||
Problem: ...
|
||||
Tried: ...
|
||||
Question: ..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Completion Protocol (MANDATORY)
|
||||
|
||||
When your work is done, follow this checklist — **step 4 is REQUIRED**:
|
||||
|
||||
⚠️ **DO NOT commit if lint or tests fail. Fix issues first.**
|
||||
|
||||
```
|
||||
[ ] 1. Run quality gates (ALL must pass):
|
||||
- npm projects: npm run lint && npm run format && npm test
|
||||
- Go projects: go test ./... && go vet ./...
|
||||
[ ] 2. Stage changes: git add <files>
|
||||
[ ] 3. Commit changes: git commit -m "msg (issue-id)"
|
||||
[ ] 4. Self-clean: gt done ← MANDATORY FINAL STEP
|
||||
```
|
||||
|
||||
**Quality gates are not optional.** Worktrees may not trigger pre-commit hooks,
|
||||
so you MUST run lint/format/tests manually before every commit.
|
||||
|
||||
**Project-specific gates:** Read CLAUDE.md and AGENTS.md in the repo root for
|
||||
the project's definition of done. Many projects require a specific test harness
|
||||
(not just `go test` or `dotnet test`). If AGENTS.md exists, its "Core rule"
|
||||
section defines what "done" means for this project.
|
||||
|
||||
The `gt done` command pushes your branch, creates an MR bead in the MQ, nukes
|
||||
your sandbox, and exits your session. **You are gone after `gt done`.**
|
||||
|
||||
### Do NOT Push Directly to Main
|
||||
|
||||
**You are a polecat. You NEVER push directly to main.**
|
||||
|
||||
Your work goes through the merge queue:
|
||||
1. You work on your branch
|
||||
2. `gt done` pushes your branch and submits an MR to the merge queue
|
||||
3. Refinery merges to main after Witness verification
|
||||
|
||||
**Do NOT create GitHub PRs either.** The merge queue handles everything.
|
||||
|
||||
### The Landing Rule
|
||||
|
||||
> **Work is NOT landed until it's in the Refinery MQ.**
|
||||
|
||||
**Local branch → `gt done` → MR in queue → Refinery merges → LANDED**
|
||||
|
||||
---
|
||||
|
||||
## Self-Managed Session Lifecycle
|
||||
|
||||
> See [Polecat Lifecycle](docs/polecat-lifecycle.md) for the full three-layer architecture.
|
||||
|
||||
**You own your session cadence.** The Witness monitors but doesn't force recycles.
|
||||
|
||||
### Persist Findings (Session Survival)
|
||||
|
||||
Your session can die at any time. Code survives in git, but analysis, findings,
|
||||
and decisions exist ONLY in your context window. **Persist to the bead as you work:**
|
||||
|
||||
```bash
|
||||
# After significant analysis or conclusions:
|
||||
bd update <issue-id> --notes "Findings: <what you discovered>"
|
||||
# For detailed reports:
|
||||
bd update <issue-id> --design "<structured findings>"
|
||||
```
|
||||
|
||||
**Do this early and often.** If your session dies before persisting, the work is lost forever.
|
||||
|
||||
**Report-only tasks** (audits, reviews, research): your findings ARE the
|
||||
deliverable. No code changes to commit. You MUST persist all findings to the bead.
|
||||
|
||||
### When to Handoff
|
||||
|
||||
Self-initiate when:
|
||||
- **Context filling** — slow responses, forgetting earlier context
|
||||
- **Logical chunk done** — good checkpoint
|
||||
- **Stuck** — need fresh perspective
|
||||
|
||||
```bash
|
||||
gt handoff -s "Polecat work handoff" -m "Issue: <issue>
|
||||
Current step: <step>
|
||||
Progress: <what's done>"
|
||||
```
|
||||
|
||||
Your pinned molecule and hook persist — you'll continue from where you left off.
|
||||
|
||||
---
|
||||
|
||||
## Dolt Health: Your Part
|
||||
|
||||
Dolt is git, not Postgres. Every `bd create`, `bd update`, `gt mail send` generates
|
||||
a permanent Dolt commit. You contribute to Dolt health by:
|
||||
|
||||
- **Nudge, don't mail.** `gt nudge` costs zero. `gt mail send` costs 1 commit forever.
|
||||
Only mail when the message must survive session death (HELP to Witness).
|
||||
- **Don't create unnecessary beads.** File real work, not scratchpads.
|
||||
- **Close your beads.** Open beads that linger become pollution.
|
||||
|
||||
See `docs/dolt-health-guide.md` for the full picture.
|
||||
|
||||
## Do NOT
|
||||
|
||||
- Push to main (Refinery does this)
|
||||
- Work on unrelated issues (file beads instead)
|
||||
- Skip tests or self-review
|
||||
- Guess when confused (ask Witness)
|
||||
- Leave dirty state behind
|
||||
|
||||
---
|
||||
|
||||
## 🚨 FINAL REMINDER: RUN `gt done` 🚨
|
||||
|
||||
**Before your session ends, you MUST run `gt done`.**
|
||||
|
||||
---
|
||||
|
||||
Rig: test
|
||||
Polecat: furiosa
|
||||
Role: polecat
|
||||
217
index.html
Normal file
217
index.html
Normal file
@@ -0,0 +1,217 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tic Tac Toe</title>
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
#app {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #555;
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
|
||||
.board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 100px);
|
||||
grid-template-rows: repeat(3, 100px);
|
||||
gap: 6px;
|
||||
margin: 0 auto 1.5rem;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.cell {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: white;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 8px;
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.cell:hover:not(.taken) {
|
||||
background: #e8f0fe;
|
||||
}
|
||||
|
||||
.cell.taken {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.cell.x {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.cell.o {
|
||||
color: #2980b9;
|
||||
}
|
||||
|
||||
.cell.winning {
|
||||
background: #d4edda;
|
||||
border-color: #28a745;
|
||||
}
|
||||
|
||||
.btn-restart {
|
||||
padding: 0.6rem 1.8rem;
|
||||
font-size: 1rem;
|
||||
background: #333;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.btn-restart:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.score {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.score span {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1>Tic Tac Toe</h1>
|
||||
|
||||
<div class="score">
|
||||
<div>X : <span>{{ score.X }}</span></div>
|
||||
<div>Égalités : <span>{{ score.draw }}</span></div>
|
||||
<div>O : <span>{{ score.O }}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="status">{{ statusMessage }}</div>
|
||||
|
||||
<div class="board">
|
||||
<div
|
||||
v-for="(cell, index) in board"
|
||||
:key="index"
|
||||
class="cell"
|
||||
:class="[
|
||||
cell ? 'taken' : '',
|
||||
cell ? cell.toLowerCase() : '',
|
||||
winningCells.includes(index) ? 'winning' : ''
|
||||
]"
|
||||
@click="makeMove(index)"
|
||||
>
|
||||
{{ cell }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn-restart" @click="restart">Rejouer</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp, ref, computed } = Vue;
|
||||
|
||||
const WINNING_LINES = [
|
||||
[0, 1, 2], [3, 4, 5], [6, 7, 8], // rows
|
||||
[0, 3, 6], [1, 4, 7], [2, 5, 8], // columns
|
||||
[0, 4, 8], [2, 4, 6], // diagonals
|
||||
];
|
||||
|
||||
createApp({
|
||||
setup() {
|
||||
const board = ref(Array(9).fill(null));
|
||||
const currentPlayer = ref('X');
|
||||
const gameOver = ref(false);
|
||||
const score = ref({ X: 0, O: 0, draw: 0 });
|
||||
const winningCells = ref([]);
|
||||
|
||||
function checkWinner(b) {
|
||||
for (const [a, c, d] of WINNING_LINES) {
|
||||
if (b[a] && b[a] === b[c] && b[a] === b[d]) {
|
||||
return { winner: b[a], line: [a, c, d] };
|
||||
}
|
||||
}
|
||||
if (b.every(cell => cell !== null)) {
|
||||
return { winner: null, line: [] };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function makeMove(index) {
|
||||
if (gameOver.value || board.value[index]) return;
|
||||
|
||||
board.value[index] = currentPlayer.value;
|
||||
|
||||
const result = checkWinner(board.value);
|
||||
if (result !== null) {
|
||||
gameOver.value = true;
|
||||
if (result.winner) {
|
||||
winningCells.value = result.line;
|
||||
score.value[result.winner]++;
|
||||
} else {
|
||||
score.value.draw++;
|
||||
}
|
||||
} else {
|
||||
currentPlayer.value = currentPlayer.value === 'X' ? 'O' : 'X';
|
||||
}
|
||||
}
|
||||
|
||||
function restart() {
|
||||
board.value = Array(9).fill(null);
|
||||
currentPlayer.value = 'X';
|
||||
gameOver.value = false;
|
||||
winningCells.value = [];
|
||||
}
|
||||
|
||||
const statusMessage = computed(() => {
|
||||
const result = checkWinner(board.value);
|
||||
if (result && result.winner) {
|
||||
return `Joueur ${result.winner} a gagné !`;
|
||||
}
|
||||
if (result && result.winner === null) {
|
||||
return 'Égalité !';
|
||||
}
|
||||
return `Tour du joueur ${currentPlayer.value}`;
|
||||
});
|
||||
|
||||
return { board, currentPlayer, gameOver, score, winningCells, makeMove, restart, statusMessage };
|
||||
}
|
||||
}).mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user