この世の全てを自動化してぐうたら生活したすぎる

煩悩八百万クリエイター戦記

React アプリ

Reactチュートリアル『まるばつゲーム(三目並べ)』の追加課題をやってみる

更新日:

Reactチュートリアル『まるばつゲーム』には追加課題が6問あるので勉強がてらやってみた。内容はかなり雑な気がするので、あくまで参考程度にとどめていただければ。

チュートリアル本題

チュートリアル本題はこちらにて。

追加課題

完成するとこんな感じになると思われる。

要件は下記。

  1. それぞれの遷移に(col,row)の表示をする
  2. 遷移リストの現在選択されているものを太字に
  3. Boardをloop2回つかって書いてみる
  4. 遷移リストを昇順/降順にする
  5. 誰かが勝った時に、3つ揃ったセルをハイライトする
  6. 引き分けの時にはdrawを表示

書いてみた

少し強引ですが書いてみました。classを増やしてハイライトに色付けしたりしたのですが、書き方がこれでいいのか悩みます。あくまで参考程度に。

<span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_end"></span>
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import './Game.css';

// 課題3 -追記ここから-
const row_size = 3;
const col_size = 3;
// 課題3 -追記ここまで-

function Square(props) {
  return (
    <button className={`square ${props.highlight ? 'highlight' : ''}`} onClick={props.onClick}>
      {props.value}
    </button>
  );
}

function calculateWinner(squares){
  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) {
      return squares[a];
    }
  }
  return null;
}

function getCellsCausedWin(squares){
  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) { return lines[i]; } } return []; } // 課題6 -追記ここから- function calculateGameEnd(history_length) { if (history_length > row_size * col_size) {
    return true;
  }
  return false;
}
// 課題6 -追記ここまで-

class Board extends Component {
  
  // 課題5 -追記ここから-
  renderSquare(i, highlight = false) {
    return (
      <Square value={this.props.squares[i]} highlight={highlight} onClick={() => this.props.onClick(i)}
      />
    );
  }
  // 課題5 -追記ここまで-

  render() {
    let cellsCausedWin = getCellsCausedWin(this.props.squares);
    
    // 課題3 -追記ここから-
    let board = [];
    let board_row = [];
    for (var row=0; row<row_size; row++) {
      board_row = [];
      for (var col=0; col<col_size; col++) {
        let i = col + row * row_size;
        // 課題5 -追記ここから-
        let highlight = cellsCausedWin.indexOf(i) !== -1 ? true : false;
        board_row.push(this.renderSquare(i, highlight));
        // board_row.push(this.renderSquare(i)); //課題3では←のように書いていた
        // 課題5 -追記ここまで-
      }
      board.push(


<div className="board-row">{board_row}</div>



);
    }
    return (
      


<div>
        {board}
      </div>



    );
    // 課題3 -追記ここまで-
  }
}

class Game extends Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [{
        squares: Array(9).fill(null),
        col: null,
        row: null,
      }],
      stepNumber: 0,
      xIsNext: true,
      // 課題4 -追記ここから-
      asc: true,
      // 課題4 -追記ここまで-
    };
  }
  
  handleClick(i) {
    const history = this.state.history.slice(0, this.state.stepNumber + 1);
    const current = history[history.length - 1]
    const squares = current.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      history: history.concat([{
        squares: squares,
        // 課題1 -追記ここから-
        col: (i % 3) + 1,
        row: Math.floor(i / 3) + 1,
        // 課題1 -追記ここまで-
      }]),
      stepNumber: history.length,
      xIsNext: !this.state.xIsNext
    });
  }
  
  jumpTo(step) {
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0,
    });
  }
  
  /* 課題4 -追記ここから- */
  toggle_sort() {
    this.setState({
      asc: !this.state.asc,
    });
  }
  /* 課題4 -追記ここまで- */
  
  render() {
    const history = this.state.history;
    const current = history[this.state.stepNumber]
    const winner = calculateWinner(current.squares);
    // 課題6 -追記ここから-
    const is_end = calculateGameEnd(history.length)
    // 課題6 -追記ここまで-
    
    const moves = history.map((step, move) => {
      const desc = move ?
        // 課題1 -追記ここから-
        'Go to move #' + move + ` (${step.col}, ${step.row})`:
        // 課題1 -追記ここまで-
        'Go to game start';
      return (
        


<li key={move}>
          <button onClick={() => this.jumpTo(move)}
            // 課題2 -追記ここから-
            class={this.state.stepNumber === move ? 'bold' : '' }
            // 課題2 -追記ここまで-
          >
            {desc}
          </button>
        </li>



      );
    })
    let status;
    if (winner) {
      status = 'Winner: ' + winner;
    // 課題6 -追記ここから-
    } else if (is_end) {
      status = 'Draw';
    // 課題6 -追記ここまで-
    } else {
      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
    }

    return (
      


<div className="game">
        


<div className="game-board">
          <Board squares={current.squares} onClick={(i) => this.handleClick(i)}
          />
        </div>



        


<div className="game-info">
          


<div>{status}</div>



          {/* 課題4 -追記ここから- */}
          


<div><button onClick={() => this.toggle_sort()}>ASC⇔DESC</button></div>



          


<ol>{this.state.asc ? moves : moves.reverse()}</ol>



          {/* 課題4 -追記ここまで- */}
        </div>



      </div>



    );
  }
}


class App extends Component {
  render() {
    return (
      


<div className="App">
        


<header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          


<h1 className="App-title">Welcome to React</h1>



        </header>



        <Game />
      </div>



    );
  }
}

export default App;

 

@charset "UTF-8";
ol, ul {
  padding-left: 30px; }

.board-row:after {
  clear: both;
  content: "";
  display: table; }

.status {
  margin-bottom: 10px; }

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px; }

.square:focus {
  outline: none; }

.kbd-navigation .square:focus {
  background: #ddd; }

.game {
  display: flex;
  flex-direction: row;
  padding: 20px; }

.game-info {
  margin-left: 20px; }

/* 課題2 -追記ここから- */
.bold {
  font-weight: bold; }

/* 課題2 -追記ここまで- */
/* 課題5 -追記ここから- */
.highlight {
  background-color: #ffe80085; }

/* 課題5 -追記ここまで- */

 

f-arts336

f-arts336

-React, アプリ

Copyright© 煩悩八百万クリエイター戦記 , 2018 All Rights Reserved.