Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <body>
  
</body>
              
            
!

CSS

              
                .board{
  position: relative;
  width: 300px;
  height: 300px;
  margin: 50px auto;
  border: 5px solid #000;
  border-radius: 15px;
  
}

.cell{
  box-sizing: border-box;
  display: inline-block;
  vertical-align: middle;
  width: 100px;
  text-align: center;
  height: 100px;
  line-height: 100px;
  cursor: pointer;
  border: 1px solid #000;
  font-size: 50px;
}

.line{
  position: absolute;
  width: 300px;
  height: 300px;
  top: 0px;
  left: 0px;
}
              
            
!

JS

              
                
//線段組件
class Line extends React.Component{
  render(){
    let startX = this.props.startIndex%3;
    let startY = Math.floor(this.props.startIndex/3);
    let endX = this.props.endIndex%3;
    let endY = Math.floor(this.props.endIndex/3);
    return <svg className="line"><line x1={startX*100+50} y1={startY*100+50} x2={endX*100+50} y2={endY*100+50} stroke="red" strokeWidth="5"/></svg>;
  }
}

//框框組件
class Cell extends React.Component{
  render(){
    let text="";
    if(this.props.mark===0){
      text="O";
    }else if(this.props.mark===1){
      text="X";
    }
    return <div className="cell" onClick={this.click.bind(this)}>{text}</div>;
  }
  click(){
    //呼叫callback函式,updateMark
    this.props.update(this.props.index);
  }
}

//#盤組件
class Board extends React.Component{
  constructor(props){
    super(props);
    //棋盤狀態
    this.state={
          circle:0, //回合數
          marks:[-1,-1,-1,    //0 1 2
                 -1,-1,-1,    //3 4 5
                 -1,-1,-1],   //6 7 8
                              //框內標記: -1無 ,0圈 ,1叉
          winner:null  //贏家資訊
    };
  }
  render(){
    
    let cells=[];
    //建立框框組件盤
    for(let i=0; i < this.state.marks.length; i++){
      cells.push(<Cell index={i} mark={this.state.marks[i]} update={this.updateMark.bind(this)}/>);//參數=>1.index框框編號 2.mark框框中標記 3.update點擊框框呼叫更新狀態標記方法
    }
    //偵測贏家並畫出線段
    if(this.state.winner!==null){
      cells.push(<Line startIndex={this.state.winner.startIndex} endIndex={this.state.winner.endIndex}/>);//參數=>startIndex:起始線段編號, endIndex:結束線段編號
    }
    //呈現#盤組件畫面
    return <div className="board">{cells}</div>;  
  }
    //更新框框狀態標記&回合切換:參數:框框編號
  updateMark(index){
    let currentMark = this.state.marks[index];
    //更新框框狀態條件1.狀態為-1 and 2.目前無贏家出現
    if(currentMark === -1 && this.state.winner===null){
      this.setState((preState)=>{
          //mark取餘數= 0 or 1  決定回合數狀態:0=圈,1=叉
          //將標記放入變更
          let mark = preState.circle%2;
          preState.marks[index]=mark;
          //取得是否有玩家出現
          let winner = this.checkWinner(preState.marks);
          return{
              circle:preState.circle+1, //切換XO回合
              marks:preState.marks,
              winner:winner
          }
      });
    }
  }
  
  //偵測是否有贏家出現:參數:
  checkWinner(marks){
    let index;
    //回傳winner狀態物件=>{side:框框起始點,startIndex:起始線段編號,endIndex:結束線段編號}
    //偵測水平方向是否有相同標記檢查3次->012,345,678
    for(let x=0; x<3; x++ ){
      if(marks[x*3]!==-1&&marks[x*3]===marks[x*3+1]&&marks[x*3+1]===marks[x*3+2]){
        return {side:marks[x*3],
                startIndex:x*3,
                endIndex:x*3+2};   
      }
    }
    //偵測垂直方向[]是否有相同標記檢查3次->036,147,258
    for(let y=0; y<3; y++){
      if(marks[y]!==-1&&marks[y]===marks[y+3]&&marks[y+3]===marks[y+3*2]){
         return {side:marks[y],
                startIndex:y,
                endIndex:y+3*2};
      }
    }
    //偵測斜角方向是否有相同標記檢查2次->048,246
    if(marks[0]!==-1&&marks[0]===marks[4]&&marks[4]===marks[8]){
      return {side:marks[0],
                startIndex:0,
                endIndex:8};
    }else if(marks[2]!==-1&&marks[2]===marks[4]&&marks[4]===marks[6]){
      return {side:marks[2],
                startIndex:2,
                endIndex:6};
    }
    //目前無贏家
    return null;
  }
}


ReactDOM.render(<Board/>,document.body);
              
            
!
999px

Console