프로그래밍언어/React

[React 도전기] 6x6 3목(오목 변형)

놀고싶다~ 2021. 6. 21. 10:00

안녕하세요!

 

오늘은 지난 시간에 이어서 5x5 이 아닌 6x6 으로 3목을 구현해보았습니다.

 

이전에 처음으로 소스를 구현하고 알고리즘을 만들었을 때는 반나절 정도 고민을 했지만 6x6은 금방 만들게 되었습니다.

 

이번 버전에서는 어떤 버전들이 수정 되었고 어떤 흐름으로 이어지는지 알아보게 되는 시간이었습니다.

 

아래 코드를 참고하시면서 후에 만들어질 오목에 대해서도 피드백 주시면 감사하겠습니다!

 

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}

class Board extends React.Component {
  renderSquare(i) {
    return (
      <Square 
        value={this.props.squares[i]}
        onClick ={() => this.props.onClick(i)} 
      />
    );
  }

  render() {
    return (
      <div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
          {this.renderSquare(3)}
          {this.renderSquare(4)}
		  {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
          {this.renderSquare(9)}
		  {this.renderSquare(10)}
          {this.renderSquare(11)}
        </div>
        <div className="board-row">
          {this.renderSquare(12)}
          {this.renderSquare(13)}
          {this.renderSquare(14)}
		  {this.renderSquare(15)}
          {this.renderSquare(16)}
          {this.renderSquare(17)}
        </div>
        <div className="board-row">
          {this.renderSquare(18)}
          {this.renderSquare(19)}
          {this.renderSquare(20)}
          {this.renderSquare(21)}
          {this.renderSquare(22)}
          {this.renderSquare(23)}
        </div>
        <div className="board-row">
          {this.renderSquare(24)}
          {this.renderSquare(25)}
          {this.renderSquare(26)}
          {this.renderSquare(27)}
          {this.renderSquare(28)}
          {this.renderSquare(29)}
        </div>
		<div className="board-row">
          {this.renderSquare(30)}
          {this.renderSquare(31)}
          {this.renderSquare(32)}
          {this.renderSquare(33)}
          {this.renderSquare(34)}
          {this.renderSquare(35)}
        </div>
      </div>
    );
  }
}

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [{
        squares: Array(36).fill(null),
      }],
      stepNumber: 0,
      xTurn: true,
    };
  }

  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 (declareWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xTurn ? 'X' : 'O';
    this.setState({
      history: history.concat([{
        squares: squares
      }]),
      stepNumber: history.length,
      xTurn: !this.state.xTurn,
    });
  }

  jumpTo(step) {
    this.setState({
      history: [{
        squares: Array(36).fill(null),
      }],
      stepNumber: step,
      xTurn: (step%2) === 0,
    });
  }

  render() {
    const history = this.state.history;
    const current = history[history.length - 1];
    const moves = history.map((step, move) => {
      if (move === 0) {
        return (
          <div key={move}>
            <button onClick={() => this.jumpTo(move)}>{"Reset Game"}</button>
          </div>
        );
      } else {
        return null;
      }
    });

    let status;
    if (declareWinner(current.squares) === 36) {
      status = 'Tie Game! Reset?';
    } else if (declareWinner(current.squares) != null) {
      status = 'Winner: ' + declareWinner(current.squares) + '  ...Play Again?';
    } else {
      status = 'Your turn: ' + (this.state.xTurn ? '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>
          <ol>{moves}</ol>
        </div>
      </div>
    );
  }
}

// ========================================

ReactDOM.render(
  <Game />,
  document.getElementById('root')
);

function declareWinner(squares) {
  
  /* 가로 , 세로 로직 구현 위한 변수 선언 */
  
  // 3목 전체조건 담는 배열 
  const samMok = [];
  
  // 오목판 갯수
  const intaglio = 36;
  
  // 가로 , 세로 로직에 저장할 배열.
  var liness = [];
  var count = 0;
  
  // 한 줄의 끝 값으로 더이상 승리하는 경우의 수가 나오지 않을 때 종료하는 변수.
  var end = -3;
  
  // 종료하는 조건의 값(해당하는 가로 첫 줄의 끝값)
  var endIf = 4;
  
  // 가로 , 세로 전체 줄 수
  var totalLine = 6;
  
  // 3목으로 한 줄당 이길 경우의 수 (가로, 세로)
  var winNumberOfCases = 4;
  
  // lines 통한 새로운 배열 출력(전체 배열)
  const lines = new Array(); 
  
  // 오목판 전체 갯수 출력
  for(let x = 0; x < intaglio; x++) {
    samMok.push(x);
  }
  
  // 가로 3목 로직
  // for문 1번째 : 가로 5번
  for(let k = 0; k < totalLine; k++) {
	  // 3목 가로 1줄당 3목이 될 경우의수 3번이므로 3번 반복 
	  for(let z = 0; z <= winNumberOfCases; z++) {
      // 3목판 전체에 대한 카운트
      for(let j = count; j < intaglio; j++) {
      // samMok에 저장된 값 나누어서 배열에 저장
      liness = samMok.slice(j,j+3);
      lines.push(liness);
      // 가로 한줄의 값이 (ex - 4,9,14) 등 끝날때 마다 한줄의 반복문 종료 조건문
	  // 7x7목 할 때 다시 구현하기. endif를 손봐야함.
	  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      if((end % endIf == 0)){
        count+=(endIf -2);
        break;
       }
      break;
      }  
      count+=1;  
      end+=1;
	  }
  }
  
   // 세로 3목 로직
  var height = 0;
  var heightval = [];

  //세로 줄 표현(0,5,10,15,20 ....)  
  for(let y=0; y<totalLine; y++) {
    for(let z=0; z<totalLine; z++) {
      // heightval 에 값 넣기.
      heightval.push(height);
      height+=totalLine;
      if(height >= intaglio) {
        break;
      }
    }
	//새로운 세로줄  시작 할 때 초기값 설정 공식 : (height = (height % 제일 밑 가로줄 마지막 첫 숫자 입력) - (전체 가로 / 세로 줄 -1)
	//(전체 가로 / 세로 줄 -1) 하는 이유 : 위에서 height+=totalLine 을 더한 값이 있기때문에 빼주는 작업을 함.
    height=(height%30) - (totalLine-1);
  }

 // 세로줄 초기값 설정.
  count = 0;
  end = -3;
  endIf = 4;
  
 // for문 1번째 : 세로 5번
  for(let w = 0; w < totalLine; w++) {
    // 3목 세로 1줄당 3목이 될 경우의수 3번이므로 3번 반복 
	  for(let e = 0; e <= winNumberOfCases; e++) {
      // 3목판 전체에 대한 카운트
      for(let r = count; r < intaglio; r++) {
      // samMok에 저장된 값 나누어서 배열에 저장
      liness = heightval.slice(r,r+3);
      lines.push(liness);
      // 세로 한줄의 값이 끝날때 마다 한줄의 반복문 종료 조건문
      if((end % endIf == 0)){
        count+=(endIf -2);
        break;
       }
      break;
      }  
      count+=1;  
      end+=1;
	  }
  }
  
  /* 대각선 로직 구현 위한 변수 선언 */
  
	// 대각선 배열의 숫자를 빼기 위해 선언한 a,b,c
	var a = 0;
	var b = 0;
	var c = 0;
	// 대각선 count 수.
	var crossCount = 0;
	// crossCount의 증감 숫자.
	var crossCountPlus = 6;
	var crossLines = [];
	// 대각선(/) 증감 숫자
	var leftCross = 7;
	// 대각선(\) 증감 숫자 
	var rightCross = 5;
	// 가로 한 줄당 3목이 될 수 있는 대각선의 수.
	var crossLope = totalLine - 2;
  
  // 대각선(\) 로직 -leftCross
  // 각각의 배열의 숫자를 slice로 구현하기 힘들기 때문에 따로 배열을 출력하여 다시 배열에 주입하였음
  // 배열의 다음 값이 6만큼 증가 하므로 아래와 같이 구현.
  // 첫번째 for는 승리하는 숫자인 가로 숫자가 연속으로 3번 있기 때문에 3번 루프를 걸어둠(ex - 0,5,10 / 5,10,15 / 10,15,20)
	for(let x = 0; x < crossLope; x++) {
		for(let y = 0; y < crossLope; y++) {
			a = samMok.indexOf((y) + (crossCount));
			b = samMok.indexOf((y) + (leftCross*1) + (crossCount));
			c = samMok.indexOf((y) + (leftCross*2) + (crossCount));
			lines.push([a,b,c]);

		}
		crossCount+=crossCountPlus;
	}
  
  // crossCount 초기값 설정
  crossCount = 0;
  // 대각선(/) 로직 - rightCross
  // 각각의 배열의 숫자를 slice로 구현하기 힘들기 때문에 따로 배열을 출력하여 다시 배열에 주입하였음.
  // 배열의 다음 값이 4만큼 증가 하므로 아래와 같이 구현.
  // 첫번째 for는 승리하는 숫자인 가로 숫자가 연속으로 3번 있기 때문에 3번 루프를 걸어둠(ex - 2,3,4 / 7,8,9 / 12,13,14)
  for(let x = 0; x < crossLope; x++) {
    // y 초기값 2로 설정한 이유 : 5x5 오목판에서 3목 승리하는 조건이 [2,6,10] 이므로 가장 낮은 값인 2를 주었음.
		for(let y = 2; y < crossLope + 2 ; y++) {
			a = samMok.indexOf((y) + (crossCount));
			b = samMok.indexOf((y) + (rightCross*1) + (crossCount));
			c = samMok.indexOf((y) + (rightCross*2) + (crossCount));
      lines.push([a,b,c]);
		}
		crossCount+=crossCountPlus;
	}
  
  
  for (let i = 0; i < lines.length; i++) {
    const [a,b,c] = lines[i];
    
    if (squares[a] && 
      squares[a] === squares[b] 
      && squares[a] === squares[c]) {
      return squares[a];
    } 
  }
  for (let i = 0; i < intaglio; i++) {
    if (squares[i] === null) {
      return null;
    }
  }
  return intaglio; //In the case of a tie, return intaglio
}

 

저는 이제 다음 포스팅에 7x7 3목을 끝으로 이후에는 진짜 사용하게 되는 오목에 대해서 구현을 해보고자 합니다!

 

처음엔 오목 알고리즘이 어렵다면 3목으로 시도해보시는 것이 좋을꺼같습니다!

 

수고많으셨습니다!