在这里阅读效果更佳

还记得当年和同桌在草稿纸上下三子棋的时光吗

今天我们就用代码来重温一下年少(假设你有react基础,没有也行,只要你会三大框架的任意一种,上手react不难)

游戏规则

  • 双方各执一子,在九宫格内一方三子连成线则游戏结束
  • 九宫格下满未有三子连线则视为平局

你可以点击这里来体验最终效果,样式有点丑,请见谅

准备阶段

建议先全局安装typescript 和 create-react-app(安装过请忽略

npm install typescript create-react-app -g

使用typescript初始化项目

create-react-app demo --typescript

初始化成功后ts环境已经配好了,不需要你手动加ts配置


此时就是tsx语法,我们就可以愉快的写ts了


src文件夹就是开发目录,所有代码都写在src文件夹下


我们使用sass来写样式,先安装sass

npm install node-sass --save

运行项目

npm run start

删掉初始化界面的一些代码

开发阶段

组件化

开发一个项目其实就是开发组件


把一个项目拆分一个个小组件,方便后期维护以及复用

  1. 棋子组件
  2. 棋盘组件
  3. 游戏规则组件
  4. 游戏状态组件

react中组件分为类组件和函数组件


需要管理状态的最好使用类组件


所以我们先把App改成类组件

import React from 'react';
import './App.css';
class App extends React.Component{
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | {} | React.ReactNodeArray | React.ReactPortal | boolean | null | undefined {
return (
<div className="App">
</div>
);
}
}; export default App;

开发棋子组件

在src下新建component文件夹,在component文件夹下新建ChessComp.tsx,ChessComp.css


以后我们的组件都放在component文件夹下


棋子组件我们使用函数组件,思考需要传入组件的属性的类型:

  1. type(棋子的类型)
  2. onClick(点击棋子触发的回调函数)

棋子类型有三种(红子 ,黑子, 空),


为了约束棋子类型,我们使用一个枚举类型,


在src下新建types文件夹,专门放类型约束,


在types下新建enums.ts约束棋子类型

export enum ChessType {
none,
red,
black
}

并在棋子tsx中导入


传入tsx的所有属性用一个IProps接口约束

interface IProps {
type: ChessType
onClick?: () => void
}

全部tsx代码:

import React from 'react';
import {ChessType} from "../types/enums";
import './ChessComp.css'; interface IProps {
type: ChessType
onClick?: () => void
}
function ChessComp ({type, onClick}: IProps) {
let chess = null;
switch (type) {
case ChessType.red:
chess = <div className="red chess-item"></div>;
break;
case ChessType.black:
chess = <div className="black chess-item"></div>;
break;
default:
chess = null;
}
return (
<div className="chess" onClick={() => {
if (type === ChessType.none && onClick) {
onClick();
}
}}>
{chess}
</div>
)
};
export default ChessComp;

其中棋子只有为none类型时才能被点击

scss 代码:


棋子我们用背景颜色径向渐变来模拟

$borderColor: #dddddd;
$redChess: #ff4400;
$blackChess: #282c34;
.chess{
display: flex;
justify-content: center;
align-items: center;
width: 50px;
height: 50px;
border: 2px solid $borderColor;
box-sizing: border-box;
cursor: pointer;
.chess-item{
width: 30px;
height: 30px;
border-radius: 50%;
}
.red{
background: radial-gradient(#fff, $redChess);
}
.black{
background: radial-gradient(#fff, $blackChess);
}
}

开发棋盘组件

同理在component文件夹下新建BoardComp.tsx,BoardComp.scss


棋盘组件我们需要传递三个参数:

  1. 棋子的数组
  2. 游戏是否结束
  3. 点击事件函数

循环数组渲染棋子, 并给游戏是否结束一个默认值

全部tsx代码:

import React from 'react';
import {ChessType} from "../types/enums";
import ChessComp from "./ChessComp";
import "./BoardComp.scss";
interface IProps {
chesses: ChessType[];
isGameOver?: boolean
onClick?: (index: number) => void
}
const BoardComp: React.FC<IProps> = function(props) {
// 类型断言
const isGameOver = props.isGameOver as boolean;
// 非空断言
// const isGameOver = props.isGameOver!;
const list = props.chesses.map((type, index) => {
return (
<ChessComp
type={type}
key={index}
onClick={() => {
if (props.onClick && !isGameOver) {
props.onClick(index)
}
}}/>
)
});
return (
<div className="board">
{list}
</div>
)
};
BoardComp.defaultProps = {
isGameOver: false
};
export default BoardComp;

scss 代码:


使用flex布局

.board{
display: flex;
flex-wrap: wrap;
width: 150px;
height: 150px;
}

开发游戏规则组件

在component文件夹下新建Game.tsx,Game.scss


游戏规则组件不需要传参,我们使用类组件来管理状态


在types文件夹下的enums.ts里新增游戏状态的枚举类型

export enum ChessType {
none,
red,
black
}
export enum GameStatus {
/**
* 游戏中
*/
gaming,
/**
* 红方胜利
*/
redWin,
/**
* 黑方胜利
*/
blackWin,
/**
* 平局
*/
equal,
}

核心的代码就是如何判断游戏的状态,我的方法有点死,你们可以自己重构,

import React from 'react';
import {ChessType, GameStatus} from "../types/enums";
import BoardComp from "./BoardComp";
import GameStatusComp from "./GameStatusComp";
import './Game.scss'; /**
* 棋子的数组
* 游戏状态
* 下一次下棋的类型
*/
interface Istate {
chesses: ChessType[],
gameStatus: GameStatus,
nextChess: ChessType.red | ChessType.black
}
class Game extends React.Component<{}, Istate> {
state: Istate = {
chesses: [],
gameStatus: GameStatus.gaming,
nextChess: ChessType.black
}; /**
* 组件挂载完初始化
*/
componentDidMount(): void {
this.init();
}
/**
* 初始化9宫格
*/
init() {
const arr: ChessType[] = [];
for (let i = 0; i < 9; i ++) {
arr.push(ChessType.none)
}
this.setState({
chesses: arr,
gameStatus: GameStatus.gaming,
nextChess: ChessType.black
})
} /**
* 处理点击事件,改变棋子状态和游戏状态
*/
handleChessClick(index: number) {
const chesses: ChessType[] = [...this.state.chesses];
chesses[index] = this.state.nextChess;
this.setState(preState => ({
chesses,
nextChess: preState.nextChess === ChessType.black? ChessType.red : ChessType.black,
gameStatus: this.getStatus(chesses, index)
}))
} /**
* 获取游戏状态
*/
getStatus(chesses: ChessType[], index: number): GameStatus {
// 判断是否有一方胜利
const horMin = Math.floor(index/3) * 3;
const verMin = index % 3;
// 横向, 纵向, 斜向胜利
if ((chesses[horMin] === chesses[horMin + 1] && chesses[horMin + 1] === chesses[horMin + 2]) ||
(chesses[verMin] === chesses[verMin + 3] && chesses[verMin + 3] === chesses[verMin + 6]) ||
(chesses[0] === chesses[4] && chesses[4] === chesses[8] && chesses[0] !== ChessType.none) ||
((chesses[2] === chesses[4] && chesses[4] === chesses[6] && chesses[2] !== ChessType.none))) {
return chesses[index] === ChessType.black ? GameStatus.blackWin : GameStatus.redWin;
}
// 平局
if (!chesses.includes(ChessType.none)) {
return GameStatus.equal;
}
// 游戏中
return GameStatus.gaming;
}
render(): React.ReactNode {
return <div className="game">
<h1>三子棋游戏</h1>
<GameStatusComp next={this.state.nextChess} status={this.state.gameStatus}/>
<BoardComp
chesses={this.state.chesses}
isGameOver={this.state.gameStatus !== GameStatus.gaming}
onClick={this.handleChessClick.bind(this)}/>
<button onClick={() => {
this.init()}
}>重新开始</button>
</div>;
}
} export default Game;

样式

.game{
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
top: 100px;
width: 250px;
height: 400px;
left: 50%;
transform: translateX(-50%);
}

开发显示游戏状态的组件

这个组件用来显示状态,在component文件夹下新建GameStatus.tsx,GameStatus.scss


没什么好说的,直接上代码

import React from 'react';
import {ChessType, GameStatus} from "../types/enums";
import './GameStatus.scss';
interface Iprops {
status: GameStatus
next: ChessType.red | ChessType.black
}
function GameStatusComp(props: Iprops) {
let content: JSX.Element;
if (props.status === GameStatus.gaming) {
if (props.next === ChessType.red) {
content = <div className="next red">红方落子</div>
} else {
content = <div className="next black">黑方落子</div>
}
} else {
if (props.status === GameStatus.redWin) {
content = <div className="win red">红方胜利</div>
} else if (props.status === GameStatus.blackWin) {
content = <div className="win black">黑方胜利</div>
} else {
content = <div className="win equal">平局</div>
}
}
return (
<div className="status">
{content}
</div>
)
} export default GameStatusComp;
.status {
width: 150px;
.next,.win{
font-size: 18px;
}
.win{
border: 2px solid;
border-radius: 5px;
width: 100%;
padding: 10px 0;
}
.equal{
background-color: antiquewhite;
}
.red{
color: #ff4400;
}
.black{
color: #282c34;
}
}

收尾

最后在app.tsx里调用game组件

import React from 'react';
import './App.scss';
import Game from "./component/Game"; class App extends React.Component{
render(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | {} | React.ReactNodeArray | React.ReactPortal | boolean | null | undefined {
return (
<div className="App">
<Game/>
</div>
);
}
}; export default App;

React + Ts 实现三子棋小游戏的更多相关文章

  1. C语 三子棋小游戏

    #include <stdio.h> #include <Windows.h> #include<time.h> #define row 3 #define lis ...

  2. 根据之前的博文,我把给同学做的三子棋小游戏的代码发出来,只是界面很丑很丑,AI算法很笨很笨,过几天我传到网盘上,提供大家下载娱乐

    background_image_filename = 'blackground.png' black_mouse_image_filename = 'black.png' white_mouse_i ...

  3. 井字棋小游戏(C语言)

    最近沉迷于<NetHack>.<DCSS>等字符游戏,对其很感兴趣,于是用C语言写了个字符界面的井字棋小游戏.欢迎大家指教. 编写时遇到了一些问题,我原先准备用循环,直到读取到 ...

  4. c语言小游戏-三子棋的完成

    三子棋的实现 一.实现思路 1.初始化数组 三子棋是九宫格的格式,所以用二维数组接收数据.用‘O’代表电脑下的子,‘X’代表玩家下的子.未下子的时候初始化 ’ ‘(space).则二维数组为“char ...

  5. [HTML5实现人工智能]小游戏《井字棋》发布,据说IQ上200才能赢

    一,什么是TicTacToe(井字棋)   本 游戏 为在下用lufylegend开发的第二款小游戏.此游戏是大家想必大家小时候都玩过,因为玩它很简单,只需要一张草稿纸和一只笔就能开始游戏,所以广受儿 ...

  6. 『HTML5实现人工智能』小游戏《井字棋》发布,据说IQ上200才能赢【算法&代码讲解+资源打包下载】

    一,什么是TicTacToe(井字棋) 本游戏为在下用lufylegend开发的第二款小游戏.此游戏是大家想必大家小时候都玩过,因为玩它很简单,只需要一张草稿纸和一只笔就能开始游戏,所以广受儿童欢迎. ...

  7. 带你使用h5开发移动端小游戏

    带你使用h5开发移动端小游戏 在JY1.x版本中,你要做一个pc端的小游戏,会非常的简单,包括说,你要在低版本的浏览器IE8中,也不会出现明显的卡顿现象,你只需要关心游戏的逻辑就行了,比较适合逻辑较为 ...

  8. Egret白鹭H5小游戏开发入门(二)

    前言: 昨天的文章中简单的介绍了Egret白鹭引擎从安装到基本的使用配置等问题,今天着重介绍H5小游戏开发的起步阶段,如Wing面板的使用,素材的处理,类的说明,开始布局等等. 整体概况: 根据上一篇 ...

  9. Egret白鹭H5小游戏开发入门(三)

    前言: 在上一篇文章中着重介绍了H5小游戏开发的起步阶段,如Wing面板的使用,素材的处理,类的说明等等,那么今天主要是涉及到场景的创建,loading的修改等等的代码编写. 对于这一节,我在讲解的过 ...

随机推荐

  1. liteos CPU占用率(十六)

    1. 概述 1.1 基本概念 CPU(中央处理器, Central Processing Unit)占用率可以分为系统CPU占用率和任务CPU占用率两种. 系统CPU占用率(CPU Percent)是 ...

  2. 密度峰值聚类算法MATLAB程序

    密度峰值聚类算法MATLAB程序 凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 密度峰值聚类算法简介见:[转] 密度峰值聚类算法(DPC) 数据见:MATL ...

  3. httpd虚拟主机起不来!!

    前几天在公司,练习负载均衡配置.在配置虚拟主机的web服务(apache) ,创建好虚拟主机的配置文件 ss -tnl  查看监控端口80已起来,通过本地浏览器访问一直显示默认的欢迎页... 一个下午 ...

  4. arXiv网站

    arXiv 原先是由物理学家保罗·金斯巴格在1991年建立的网站, 我们会将预稿上传到arvix作为预收录,因此这就是个可以证明论文原创性(上传时间戳)的文档收录网站.

  5. [C6] Andrew Ng - Convolutional Neural Networks

    About this Course This course will teach you how to build convolutional neural networks and apply it ...

  6. 剑指Offer-19.顺时针打印矩阵(C++/Java)

    题目: 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字 ...

  7. luoguP5227 [AHOI2013]连通图(线性基做法)

    题意 神仙哈希做法. 随便找个生成树,给每个非树边赋一个值,树边的值为所有覆盖它的边的值得异或和. 删去边集使得图不联通当且即当边集存在一个子集异或和为0,可以用线性基. 证明的话好像画个图挺显然的 ...

  8. Python __slots__

    Python 类的特殊变量:__slots__ 使用 __slots__ 后,类中 __weakref__ 和 __dict__ 消失,同时阻止动态属性绑定 由于 __dict__ 记录着类中所有的属 ...

  9. vue项目中常见问题及解决方案

    webpack项目中自动引入全局scss变量文件 假设我们有一个公共的scss变量文件variables.scss /*存放所有全局变量*/ $card-title:#C7D200; //首页 卡片标 ...

  10. 【2019.8.14 慈溪模拟赛 T2】黑心老板(gamble)(2-SAT)

    \(2-SAT\) 考虑每个点只能选择\(R\)或\(B\),可以看作选\(0\)或\(1\). 然后对于给出的关系式,若其中一个位置满足关系式,另两个位置就必须不满足关系式,这样就可以对于每个关系式 ...