preface

Js +Ts+ESlint “tourism official website” combat project based on the previous implementation of rax.js +Ts+ESlint “tourism official website” combat project based on the React official project example GoBang tic-Tac-toe game to further familiarize yourself with the Rax framework.

The tic-Tac-toe mini-game has been written in detail in the official documentation, so this article will not cover it in detail. It is mainly about reconstructing the mini-game in Rax and replacing the project case with the function component and hooks.

The function effect is as follows:

  1. All the features of tic-Tac-TOE
  2. Can determine when a player has won
  3. Record the progress of the game
  4. Allows players to view the history of the game, as well as the state of any historical version of the game board

The goal of this article is to further optimize and improve the function points of the upgrade mini-game project:

  1. Displays the coordinates of each move in the game history list in the format (column number, row number).
  2. Shows the currently selected item in bold in the history list.
  3. Use two loops to render the squares of the board instead of hardcode.
  4. Add a button that displays history in ascending or descending order.
  5. Each time someone wins, highlight the three pieces in a line.
  6. Displays a draw message when no one wins.

project

Gobang small game embedded tourism projects

Key points: Use Rax route to jump.

The routing configuration

/src/app.json

{
  "routes": [{"path": "/"."source": "pages/Home/index"
    },
    {
      "path": "/gobang"."source": "pages/Gobang/index"}]."window": {
    "title": "Suzuki 🎈"}}Copy the code

Travel navigation

/src/components/Header.tsx

import { history } from 'rax-app';

/ /... The code is omitted
// Add Gobang navigation to {/* navMenu */}
<div className={styles.navLink} key="gobang" onClick={()= > history.push('gobang')}>
  Gobang
</div>
Copy the code

Gobang navigation

Gobang returns to the travel page

/src/pages/Gobang/index.tsx

import { history as router } from 'rax-app';

/ /... omit

// Add the code to return to the travel page
<div style={{ textAlign: 'center'}}className="button buttonPrimary buttonBig buttonNoRound" onClick={()= >Router.push ('/')}> Return to the travel page</div>
Copy the code

Add chess coordinates

Important: use the I on the checkerboard click event to determine the current chess piece coordinates.

Gobang page

/src/pages/Gobang/index.tsx

// Add the nowKey attribute to the history object state to record the current coordinates.
const [history, setHistory] = useState([
  {
    squares: Array(9).fill(null),
    nowKey: ' ',}]);// Calculate the coordinates of the current piece according to 'I' in the handleClick event, and call the effect event to update the record.
const handleClick = (i: string | number) = > {
  const nowKey = [(+i % 3) + 1.Math.floor((+i / 3)) + 1].join(', ');
  / /... Omit code
  const historyTemp = [...curHistory, { squares, nowKey }];
  setHistory(historyTemp);
}

// Display the coordinates of each move in the game history list
const moves = history.map((step, move) = > {
  const desc = move ? `Go to move #${move} \n Chess position: (${step.nowKey}) ` : 'Go to game start';
  / /... Omit code
});
Copy the code

We need to update the li margin and the width and height style of the Li > button.

Displays the currently selected history item

Focus: Historical items on style focus, pseudo-elements and pseudo-classes can be applied directly to add.

Gobang style

/src/pages/Gobang/index.module.css

li > button {
  font-size: 16px;
  width: 160px;
  height: 100%;
  position: relative;
}
li > button:focus:before {
  content: "";
  position: absolute;
  top: -4px;
  left: -4px;
  right: -4px;
  bottom: -4px;
  border: 4px solid gold;
  transition: all 0.5 s;
  animation: clipPath 3s infinite linear;
}
@keyframes clipPath {
  0%.100% {
    clip-path: inset(0 0 95% 0);
  }

  25% {
    clip-path: inset(0 95% 0 0);
  }
  50% {
    clip-path: inset(95% 0 0 0);
  }
  75% {
    clip-path: inset(0 0 0 95%); }}Copy the code

Loop render grid

Key: array.map () and the corresponding key

Board, Board

/src/components/Board/index.tsx

// omit the code
// Add a key to the grid
<Square key={i} value={props.squares[i]} onClick={() = > props.onClick(i)} />;

 <View>{[0, 1, 2]. Map ((v) => {return ())<div key={v} className={styles.boardRow}>
        {[NaN, NaN, NaN].map((v2, i) => {
          return (renderSquare(v * 3 + i));
        })};
      </div>
    );
  })}
</View>
Copy the code
  • Note: Do not useArray(3)To initialize an empty array, which cannot be rendered when empty
  • The key value cannot be index
  • Notice the incoming methodrenderSquareTo the index value of the outer loop

Sort history

Important: Set the sort state and call array.reverse () to reverse the history Array

Gobang page

/src/pages/Gobang/index.tsx

// Set the sort state
const [sort, setSort] = useState(false);

// Determine if you need to roll the history array
sort && moves.reverse();

/ /... Omit code
// Sort buttons
<div
  style={{ textAlign: 'center', width: '100px'}}className="button buttonPrimary buttonNoBig buttonRound"
  onClick={()= >setSort(! Sort sort)} ></div>
Copy the code

Identify the winning piece

Key points: When winning, obtain the number of the winning piece and pass it to Square for judgment to change the style of the piece

style

/src/global.css

/* Sets the winning style */
.winner {
  background: firebrick ! important;
  color: white;
}
Copy the code

Attention needs! Important Otherwise priority styles will be overwritten.

Winning piece number

/src/utils/index.ts

// Determine the winning piece method
export function calculateWinner(squares: string[]) {
  const lines = [
    [0.1.2],
    [3.4.5],
    [6.7.8],
    [0.3.6],
    [1.4.7],
    [2.5.8],
    [0.4.8],
    [2.4.6]];for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      // Change this step to return an object containing the number of the acquired piece, square
      return { winner: squares[a], square: lines[i] }; }}return null;
}
Copy the code

/src/pages/Gobang/index.tsx

Change the criteria for determining winner and pass the array of winning pieces

- if (calculateWinner(squares) || squares[i]) {
+ if(calculateWinner(squares)? .winner || squares[i]) {return;
}
  
// The current board
const current = history[stepNumber];
- const winner = calculateWinner(current.squares);
+ const calculate = calculateWinner(current.squares);
+ constwinner = calculate? .winner; +let winSquare: number[] | undefined;
  
// Determine the winner condition
if (winner) {
  status = `Winner: ${winner}`; + winSquare = calculate? .square;/ /... Omit code

/ / props
<div className={styles.gameBoard}>
- <Board squares={current.squares} onClick={(i)= > handleClick(i)} />
+ <Board squares={current.squares} winner={winSquare} onClick={(i)= > handleClick(i)} />
</div>

Copy the code

Checkerboard win

/src/components/Board/index.tsx

- function Board(props: { squares: { [x: string] :any }; onClick: (arg0: any) = >any }) {+function Board(props: { squares: { [x: string] :any}; winner? :number[], onClick: (arg0: any) = >any }) {
  const renderSquare = (i: number) = >{-return <Square key={i} value={props.squares[i]} onClick={()= > props.onClick(i)} />;
    + constwinner = props.winner? .includes(i); +return <Square key={i} winner={winner} value={props.squares[i]} onClick={()= > props.onClick(i)} />;
  };
/ /... Omit code
Copy the code

Chess, rendering

/src/components/Square/index.tsx

- function Square(props: { value: number, onClick: () => void; }) {+function Square(props: { value: number, winner? :boolean, onClick: () => void; }) {
  return(-<button className={styles.square} onClick={()= > props.onClick()}>
+ 	<button className={` ${props.winner ? 'winner' :'${}styles.square} `}onClick={()= > props.onClick()}>
      {props.value}
    </button>
  );
}
Copy the code

A draw

Important: It’s easy to judge the length of a history entry and change the display

Gobang page

/src/pages/Gobang/index.tsx

if (winner) {
  status = `Winner: ${winner}`; winSquare = calculate? .square;// Determine the length, display the content of the tie.
} else if (moves.length === 10) {
  status = 'Draw, start over game';
} else {
  status = `Next player: ${xIsNext ? 'X' : 'O'}`;
}
Copy the code

Project overview

This is how RAx.js rebuilt the React official project example GoBang Tic-Tac-toe mini game and implemented advanced features and optimized the project. The following is a brief overview of the project:

After the speech

At present, I will learn the basic knowledge of react. js and rax. js here for the time being. Later, I will grow in the actual project. I will try to summarize and share with my friends after learning.

Project repository: Github, Gitee