Skip to content

Commit

Permalink
feat: update game
Browse files Browse the repository at this point in the history
  • Loading branch information
jser committed Aug 10, 2024
1 parent 12c956e commit 4f90134
Show file tree
Hide file tree
Showing 19 changed files with 10,986 additions and 39 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/actions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: tic-tac-toe
on:
push:
branches:
- main
permissions:
contents: write
jobs:
build-and-deploy:
concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession.
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4

- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
run: |
yarn install
yarn build
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: build # The folder the action should deploy.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.idea/
/node_modules/
/build/
/.obsidian/
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,20 @@
# tic-tac-toe
# tic-tac-toe

## Requirement

This is the case from <https://react.dev/learn/tutorial-tic-tac-toe>. But I implement it without guide from the tutorial.

## Analyzing procedure

It's a good way to analyze the procedure of implementation. Just use the mind map to explore uncertain. It will helps you to reduce bug rates. I shared the whole procedure with photo.

![[/docs/images/react-tic-tac-toe.png]](/docs/images/react-tic-tac-toe.png)
## Implementation

I uploaded the code to GitHub. See it at <https://github.com/ustinian-wang/tic-tac-toe>
The implementation seems not perfect. I see. I have the plan to optimize code in the next. Just give the time, please.

## Preview

You can play it online yourself. See it at <https://ustinian-wang.github.io/tic-tac-toe/>

Binary file added docs/images/react-tic-tac-toe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname'
]
};
16 changes: 12 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"main": "/src/index.js",
"scripts": {
"start": "npx react-scripts start",
"build": "npx react-scripts build",
"test": "npx react-scripts test --env=jsdom",
"build": "cross-env PUBLIC_URL=/tic-tac-toe react-scripts build",
"test": "echo test",
"eject": "npx react-scripts eject"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
"@ustinian-wang/kit": "^0.1.4",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-scripts": "^5.0.0"
},
"browserslist": {
"production": [
Expand All @@ -23,5 +25,11 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"cross-env": "^7.0.3",
"jest": "^29.7.0",
"jest-watch-typeahead": "0.6.3",
"jest-watcher": "^29.7.0"
}
}
90 changes: 80 additions & 10 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,86 @@
import GridTable from "./components/GridTable";
import { GameProcess } from "./components/GameProcess";
import RecordHistory from "./components/RecordHistory";
import { useState } from "react";
import { getGrids } from "./business/Grid";
import { RoleManager } from "./business/RoleManager";
import { RecordManager } from "./business/RecordManager";
import { getWinner } from "./business/game";

function MyButton() {
return (
<button>
I'm a button
</button>
);
}
export default function MyApp() {
let grids = getGrids();
let role = RoleManager.getCurrRole();
let roles = RoleManager.getRoles();
let records = RecordManager.getRecords();
let currRecords = RecordManager.getCurrentRecords();
// let records = RecordHistory.getRecords();
const [gridsValue, setGridsValue] = useState(grids);
const [roleValue, setRoleValue] = useState(role);
const [rolesValue, setRolesValue] = useState(roles);
const [recordsValue, setRecordsValue] = useState(records);
const [currRecordsValue, setCurrRecordsValue] = useState(currRecords)



const onClickGrid = (index)=>{
let grid = grids[index];
let winner = getWinner(roles, grids);
if(grid.use(role.roleId) && !winner){

//交换角色
role = RoleManager.exchangeRoles();
setRoleValue(role);
roles = RoleManager.getRoles();
setRolesValue(roles);

//增加一条操作记录
RecordManager.add(role.roleId, grid.gridId);
records = RecordManager.getRecords();
currRecords = RecordManager.getCurrentRecords();
setRecordsValue(records);
setCurrRecordsValue(currRecords);
}
setGridsValue(grids);
}

const onClickRecord = (index)=>{
RecordManager.changeCursor(index);

currRecords = RecordManager.getCurrentRecords();
setCurrRecordsValue(currRecords);
initGridsByCurrRecords(currRecords);
}

function initGridsByCurrRecords(currRecords){
const gridsMapById = {};
grids.forEach(grid=>{
gridsMapById[grid.gridId] = grid;
grid.clear();
})

currRecords.forEach(record=>{
let {
roleId,
gridId
} = record;
let grid = gridsMapById[gridId];
grid.use(roleId);
})

setGridsValue(grids);
}


return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
<div style={{display: 'flex'}}>
<div>
<GameProcess role={roleValue} roles={rolesValue} grids={gridsValue}></GameProcess>
<GridTable grids={gridsValue} role={roleValue} onClickGrid={onClickGrid}/>
</div>
<div>
<RecordHistory records={recordsValue} onClickRecord={onClickRecord}></RecordHistory>
</div>
</div>

);
}
75 changes: 75 additions & 0 deletions src/business/Grid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { RoleManager } from "./RoleManager";
import { isEmptyArr } from "@ustinian-wang/kit";
import { getUid } from "../utils";

const GridStatusDef = {
BLANK: 1,
PLAYED: 2
}

export class Grid {
constructor() {
this.status = GridStatusDef.BLANK;
this.roleId = 0;
this.gridId = getUid()
}

changeSingleStatus( status ) {
if ( status !== GridStatusDef.BLANK ) {
this.status = status;
return true;
}
return false;
}

changeStatus( status ) {
this.status = status;
}

changeRole( roleId ) {
this.roleId = roleId
}

use(roleId){
if( this.status===GridStatusDef.BLANK){
this.roleId = roleId;
this.status = GridStatusDef.PLAYED;
return true;
}
return false;
}
clear(){
this.status = GridStatusDef.BLANK;
this.roleId = 0;
}

getGridContent() {
let role = RoleManager.getRole( this.roleId );
if ( this.status === GridStatusDef.BLANK ) {
return "";
} else {
if ( role ) {
return role.getName();
} else {
// console.error( "role should exist" )
return "";
}
}

}

static getInstance() {
return new Grid();
}
}

let list = []
export function getGrids(){
if(isEmptyArr(list)){
for ( let i = 0; i < 9; i++ ) {
let grid = Grid.getInstance();
list.push( grid );
}
}
return list;
}
18 changes: 18 additions & 0 deletions src/business/Record.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

export class Record {
constructor(roleId, gridId) {
this.roleId = roleId;
this.gridId = gridId;
}

changeRole(roleId){
this.roleId = roleId;
}
changeGrid(gridId){
this.gridId = gridId;
}

static getInstance(roleId, gridId){
return new Record(roleId, gridId)
}
}
47 changes: 47 additions & 0 deletions src/business/RecordManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Record } from "./Record";

class RecordManagerFactory{
constructor() {
this.list = [];
this.cursor = -1;
}

changeCursor(index){
this.cursor = index;
}

next(){
if(this.cursor>this.list.length){
return;
}
this.cursor++;
}

prev(){
if(this.cursor<=0){
return;
}
this.cursor--;
}

add(roleId, gridId){
this.clearRestOfNext()
this.cursor++;
this.list.push(Record.getInstance(roleId, gridId))
}

clearRestOfNext(){
this.list.splice(this.cursor+1);
}

getCurrentRecords(){
return this.list.slice(0, this.cursor+1);
}

getRecords(){
return this.list;
}
}

export const RecordManager = new RecordManagerFactory()

21 changes: 21 additions & 0 deletions src/business/Role.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getUid } from "../utils";

export const RoleTypeDef = {
X: 1,
O: 2
}

export class Role {
constructor( type ) {
this.type = type;
this.roleId = getUid();
}

static getInstance( type ) {
return new Role( type )
}

getName() {
return this.type === RoleTypeDef.X ? "X" : "O";
}
}
25 changes: 25 additions & 0 deletions src/business/RoleManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Role, RoleTypeDef } from "./Role";
class RoleManagerFactor{
constructor() {
this.list = [];
let roleX = Role.getInstance(RoleTypeDef.X);
let roleO = Role.getInstance(RoleTypeDef.O);
this.list.push(roleO, roleX);
}
getCurrRole(){
return this.list[0]
}
getRole( roleId ) {
return this.list.find( role => role.roleId === roleId )
}
exchangeRoles(){
this.list.reverse();
return this.list[0];
}
getRoles(){
return this.list;
}

}

export const RoleManager = new RoleManagerFactor()
Loading

0 comments on commit 4f90134

Please sign in to comment.