日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

网页版扫雷 -- React练习

發布時間:2023/12/10 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网页版扫雷 -- React练习 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

開會不準帶電腦,手機app玩掃雷玩到眼快瞎,而且每次都要忍受長達十秒的廣告,自己寫一個算了。
詳細代碼在git里,還在更新。

第一步: React & webpack setup

用webpack主要目的是為了搭建一個簡單的webpack server, 順帶著直接用它打包發布好了.

set up React

React需要安裝一系列相關包,什么ReactDom啊之類的,直接 npm install XX --save-dev安裝之就好。

set up webpack

npm install webpack --save-dev
npm install webpack-dev-server --save-dev
npm install html-webpack-plugin --save-dev

--save-dev是因為這些包只為開發用,不是給user用的
然后配置一下webpack.config.js:

entry: { //程序的入口,類似于定義java的main方法,邏輯從這里開始app: path.resolve(__dirname, 'src') + '/index.js'},devServer: {//定義webpack-serverport: 8089, // localhost:8089/會訪問到本地的頁面contentBase: [path.join(__dirname, 'site/'), // localhost:8089/index.html會訪問到server的site/index.html],},output: { //所有的js代碼都輸出到bundle.js里,然后放到site/文件夾里filename: 'bundle.js',path: path.resolve(__dirname, 'site/')},resolve: { modules: ['node_modules'],extensions: ['.js', '.jsx'], //import xx.js或者xx.jsx可以省略后綴},plugins: [ // 因為有一個html template,所以需要html-webpack-plugin來添加html文件。new HtmlWebpackPlugin({filename: 'index.html',template: 'src/index.html'//html template直接放在src根目錄里。}),],

然后在package.json里面的
scripts里面添加:

"build": "webpack -d","start:dev": "webpack -d && webpack-dev-server --progress --colors --config ./webpack.config.js"

這樣就可以用 npm run build來打包所有代碼到site里
用npm run start:dev來啟動本地的webpack server

webpack要點大概就這些了。配置好了之后就開始寫代碼了。

第二步: 生成一個布好雷的方格:

數據結構:

掃雷的數據結構沒什么可以糾結的,一個二維數組。

數據類型:

也沒啥好糾結的,數字嘛。
數字范圍就是1-8,也沒啥懸念嘛。 表示周圍有1-8個雷(玩了幾千把掃雷,只遇到過一次8)。
空白的格子就用0表示了,表示周圍沒有雷嘛。
還剩下一個9就用來表示雷好雷嘛。到此為止,一位數的數字全部用上了。

生成數組:

剩下的問題就是,怎么把這個數組生成出來。
首先,根據我幾千把掃雷的經驗,這應該是個隨機生成的數組。不大可能有什么庫之類的。
那么如何隨機生成?
每一盤,雷的個數是不變的,那也就是說,我們可以隨機找幾個為止把雷放進去,然后計算剩下的每個格子周圍有多少個雷就可以了。
事情就簡單起來了:(以簡單為例子,10*8的矩陣,10個雷。)

  • 生成一個10*8長度的數組。塞進去10個9.(怎么喜歡怎么來)
    - 可以先放進去10個9,70個0,然后shuffle 打亂順序
    - 也可以先生成一個80個0 的數組,然后隨機抽取10個位置替換為9.
  • 把這個數組每8個一截,折斷成10截,放進一個二維數組。
  • 開始計算這個二維數組所有目前為0的位置周圍有多少個9,然后替換成真正的數字。
  • 一個由0-9組成的二維數組就好了。注意數雷的時候小心處理邊界條件。

    第三步:template和js入口

    html

    react的話 html 的template就很簡單了。只有幾行:

    <!DOCTYPE html> <html> <head><meta charset="utf-8"><meta content="width=device-width, initial-scale=1" name="viewport" /><title>saolei</title> </head> <body> <div id="game-wrapper"/> </body> </html>

    第二行 meta是為了以后調mobile用的css用的
    實際上html的本體只有一行,game-wrapper。 不用提react的事,也不用提css,也不用提scripts。

    react入口:

    上面的webpack說好了,入口要從src/index.js開始, 所以這個文件就是入口了,也很簡單:

    import React from 'react'; import ReactDOM from 'react-dom' import Game from './components/Game';ReactDOM.render(<Game />, document.getElementById('game-wrapper'));

    將id為game-wrapper的元素替換為Game component. Game是從 ./components/Game里import的,但是不用提html的事情。 這兩個會在webpack打包的時候結合為一體。
    打包之后的html: site/index.html:

    <body><div id="game-wrapper"/><script type="text/javascript" src="bundle.js"></script> </body>

    第四步: Components:

    請不要吐槽變量名字

    最頂層的Components是Game
    然后看,Game要包含啥
    掃雷嘛,就一個MN的格子加上幾個按鈕(開始,難度),所以需要兩種Component: GameBoard和Button
    最后:
    GameBoard其實是有MN個小的格子組成,所以GameBoard需要一種Component: SingleGrid
    所以components包如圖:

    在下面解釋中,Game是GameBoard和Button的爸爸,GameBoard是SingleGrid的爸爸。

    Game.jsx:

    作為最頂層的Game,

  • 它要知道自己的狀態(在玩著還是已經結束(贏或者輸))記錄在status里
  • 需要知道自己的難度(多少行,多少列,多少雷)
  • import React from 'react'; import GameBoard from './GameBoard'; import Button from './Button'; import '../styles/index.css';class Game extends React.Component {constructor(props) {super(props);this.state = {totalRows: 10,totalColumns: 8,totalLei: 10,status: 'inProgress',gameIndex: 0,};//function bind this省略}gameFinish() {/*游戲結束時觸發這個方法*/}/*button們的方法在這里*/setEasyGame() {/*省略*/}setMediateGame() {/*省略*/}setHardGame() {/*省略*/}reStart() {/*省略*/}render () {return (<React.Fragment><div className={`gameboard${this.state.totalColumns}`}><GameBoard key={this.state.gameIndex} {/*把方法和屬性傳給兒子GameBoard*/}/></div><div className={`buttons${this.state.totalColumns}`}><Button content="New Game" color="green" onClick={this.reStart}/><Button content="Easy" color="green" onClick={this.setEasyGame}/>//還有倆button</div></React.Fragment>)} }export default Game

    GameBoard.jsx:

    作為大格子,需要知道它爸爸定義了的游戲等級,自己有多少行多少列(從Game那里得到)

  • 需要知道每個格子都是什么數值。 gameMatrix。 每個新的游戲都要generate一個新的矩陣。
  • 需要知道游戲進度 游戲失敗或者成功都是從一次鼠標點擊小格子的事件觸發的
    - fail: 左鍵點擊到了一個雷。
    - win: 左鍵點擊到了最后一個還沒打開的數字。
    - 都不是上面兩種情況,則是還在進行中。
  • 除此之外,還需要知道哪些格子被打開了(如果點到空白的格子,需要檢查周圍的格子)。所以,在這一層維護了一個opened數組,用來記錄被打開了的格子,和方便計算成功或者失敗。

    import React from 'react'; import SingleGrid from './SingleGrid'; import {generateGame} from "../service/dataService"; import '../styles/grid.css'class GameBoard extends React.Component {constructor(props) {super(props);const {totalRows, totalColumns, totalLei} = props;this.state = {gameMatrix: generateGame(totalRows, totalColumns, totalLei),opened: [],};//方法綁定this}gameFail(status) {/*被兒子調用,然后調用爸爸的gameFail方法*/};openGrid(position) {/*被兒子調用然后計算是不是最后一個數字格子,如果是,調用爸爸的gameWin方法*/};handleZero(position, totalRows, totalColumns){/*如果被兒子通知,點到空白的格子了,*//*則需要檢查四周格子,把所有空白格子都打開*/}render () {const { totalRows, totalColumns, gameStatus } = this.props;return (<ul> // 用兩個map加載小格子們{this.state.gameMatrix.map((row, rowNumber)=>(<p className="row" key={ rowNumber }>{row.map((grid, gridNumber) =><SingleGrid value={grid} {/*其他需要傳給小格子的屬性*/}></SingleGrid>)}</p>))}</ul>)} } export default GameBoard

    SingleGrid.jsx:

    作為最小的單元,也是和user直接交互的component,需要知道:

  • 自己的值是什么(數字或者雷或者空白)
  • 自己是被打開了還是被標記成雷了
  • 如果被打開了,是被用戶點擊打開的還是被爸爸通知打開的(檢查空白格子產生情況)
  • 另外,還需要知道整個游戲的狀態: 如果贏了或者輸了,則所有的格子都不能被點擊
    如果輸了,還需要判斷被標記的狀態和實際狀態是不是相符,如果不是需要給出錯誤判斷:
  • /*** Created by xzou2 on 11/13/18.*/ import React from 'react'; import '../styles/grid.css'class SingleGrid extends React.Component {constructor(props) {super(props);this.state = {status: 'default', opened: false};//方法綁定this}handleClick() {/*左鍵點擊,需要根據當前自己的狀態來判斷狀態變化*/}handleRightClick(e) {/*右鍵點擊,設置為小紅旗或者是取消小紅旗*/}handleTouchEnd(){/*略*/}touchStart(){/*給手機等觸屏玩兒的*/}componentWillReceiveProps(nextProps) {/*檢查從爸爸那里得來的opened屬性,*//*如果是爸爸讓自己打開,而自己還沒有打開,那么改變狀態*/}render () {const { value, gameStatus } = this.props;let currentStatue = '';if (gameStatus !== 'fail') {//判斷每個格子需要顯示的東西} else {//游戲已經輸了判斷每個格子的}return <spanclassName={`grid${currentStatue} grid ${currentStatue && currentStatue!== 'marked' ? 'open' : 'default' }`}onClick={this.handleClick}onContextMenu={this.handleRightClick}onTouchStart={this.touchStart}onTouchEnd={this.handleTouchEnd}>{/[1-8]/.test(currentStatue) || currentStatue === 'X' ? currentStatue: ''}</span>} } export default SingleGrid

    Button.jsx:

    一個簡單的component

    class Button extends React.Component {render() {const {color,content,onClick,} = this.props;return (<button className={color} onClick={onClick}>{content}</button>)} } export default Button

    第五步: 處理用戶點擊事件:

    這大概是最好玩兒最頭疼的一個步驟了。邏輯比較復雜,如果需要,請看源碼
    在最開始的時候,所有的小格子都需要藏起來,這時候小格子的狀態是沒被打開過的。

    左鍵點擊

    根據上面的component設計,用戶點擊的是每一個小格子。每個小格子都知道自己的值,用戶點擊之后:

    • 如果小格子的值是9, 那就是點到雷了,應該直接宣布失敗。這時候要一層層的告訴爸爸和爸爸的爸爸,游戲失敗了。
    • 如果小格子的值是0,那么就是點到空白地方了,這時候要往外擴散。所以除了改自己的狀態之外,還需要告訴爸爸,點到0了,讓爸爸去打開自己周圍的格子,這時候爸爸需要一個遞歸,來打開這個格子周圍的所有為0的格子以及0外面一圈的格子。這時候,這個格子周圍的格子的狀態是由爸爸改變的,而不是通過用戶點擊。
    • 如果小格子的值是1-8,這是最簡單的,直接翻開顯示數字就行。小格子的狀態改為打開。這時候有可能是最后一個還沒打開的格子,所以需要告訴爸爸,自己打開了,求檢查是不是最后一個,如果是,爸爸則會宣布游戲結束。
    • 如果這個格子已經被打開了,則什么都不能做了
    • 如果這個格子被標記成小紅旗了,則改到問號狀態,或者是標記成問號狀態,則改回到小紅旗。

    右鍵點擊事件:

    小紅旗和隱藏切換。
    右鍵點擊事件比較抓狂的是,手機不支持右鍵,這個糾結過程在另一個博客里了

    最后,圖片

    小紅旗的圖片和雷的圖片用css就可以綁定到狀態上。className可以根據情況拼一個字符串出來。

    最后的最后,戳這里可以直接開會的時候玩兒掃雷了。

    總結

    以上是生活随笔為你收集整理的网页版扫雷 -- React练习的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。