Skip to content

ash106/tic-tac-toe

Repository files navigation

Tic Tac Toe

React Tic Tac Toe gif

Demo

Built With

  • React - Used for the views
  • Ruby on Rails - Mainly used as an API to communicate with React containers
  • Bootstrap 4 - Used Reactstrap to style React components
  • D3 - Used for the donut chart on stats page
  • React Motion - Used to animate the donut chart updates

ActionCable and React Code

When someone wins a game, a POST request is sent to games#create

// client/app/bundles/TicTacToe/containers/GameContainer.jsx

componentDidUpdate(prevProps, prevState) {
  const current = this.state.history[this.state.stepNumber];
  const winner = this.calculateWinner(current.squares);
  if (winner.name) {
    $.ajax({
      url:'/games',
      type:'POST',
      dataType:'json',
      data:{
          game: {winner: winner.name,  history: this.state.history}
      }
    });
  }
}

On successful save, the create action broadcasts updated stats to the games ActionCable channel

# app/controllers/games_controller.rb

def create
  @game = Game.new(game_params)

  if @game.save
    ActionCable.server.broadcast 'games',
      winner: @game.winner,
      stats: Game.stats
    render json: @game, status: :created, location: @game
  else
    render json: @game.errors, status: :unprocessable_entity
  end
end

The StatsContainer receives the updated stats and passes them to the Stats component

// client/app/bundles/Stats/containers/StatsContainer.jsx

componentWillMount() {
  var self = this;
  if (typeof App !== 'undefined'){
    App.messages = App.cable.subscriptions.create('GamesChannel', {  
      received: function(data) {
        return self.setState({ data: data.stats });
      }
    });
  }
}

render() {        
  return <Stats data={this.state.data} />
}

The Stats component passes the new data to the D3DonutChart component

// client/app/bundles/Stats/components/Stats.jsx

<Container>
  <Row>
    <Col lg={{ size: 4, offset: 4 }} className="align-self-center">
      <h3 className="stats-label text-center">Wins</h3>
      <div className="chart-wrapper text-center">
        <D3DonutChart data={this.props.data} />
      </div>
      <ChartLabel label="X" data={this.props.data[0]} />
      <ChartLabel label="O" data={this.props.data[1]} />
      <NavLink name="game" link="/" />
    </Col>
  </Row>
</Container>

Where it is finally rendered using D3 and React Motion

// client/app/bundles/Stats/components/D3DonutChart.jsx

render() {
  var width = 290,
      height = 290,
      radius = Math.min(width, height) / 2;

  var color = d3.scale.ordinal()
      .range(["#3299BB", "#FF9900"]);

  var pie = d3.layout.pie()
      .value(d => d)
      .sort(null);

  var arc = d3.svg.arc()
      .innerRadius(radius - 100)
      .outerRadius(radius - 20);

  var displayedData = pie(this.props.data);

  return (
    <svg width={width} height={height}>
      <g transform={"translate(" + width / 2 + "," + height / 2 + ")"}>
        {displayedData.map((slice, i) =>
          <Motion
            key={i}
            defaultStyle={{
              startAngle: slice.startAngle,
              endAngle: slice.endAngle,
              padAngle: slice.padAngle,
            }}
            style={{
              startAngle: spring(slice.startAngle),
              endAngle: spring(slice.endAngle),
              padAngle: spring(slice.padAngle)
            }}>{value => <path
              fill={color(i)}
              d={arc(value)} />
            }</Motion>
        )}
      </g>
    </svg>
  );
}

Getting Started

Prerequisites

ruby -v             # 2.3.4
node -v             # 7.9.0
brew install yarn
rails -v            # 5.0.2
gem install foreman

Installing

Install dependencies

bundle && yarn

Setup database

rails db:setup

Run server

foreman start -f Procfile.dev

Play some tic tac toe

About

Tic Tac Toe made with React and ActionCable

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published