프로그래밍언어/React

[React 도전기] 7x7 3목 (오목 변형)

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

안녕하세요!

 

이제 3목 7x7 마지막입니다!

 

다음은 아마 오목 버전으로 보여드릴 수 있을 거 같습니다(희망을 가지며 ㅎ..)

 

이번 버전에서는 앞으로 원래 사용하는 오목판(19x19) 등 자유롭게 오목판을 변경할 수 있도록 새로운 function을 만들었습니다(노가다로 이제 안해도 숫자만 바꾸면 판이 바뀌어요!)

 

그리고 endIf가 붙는 조건문에서 조건을 조금 수정하였습니다.

 

한가지 아쉬운게 있다면 이제 변수를 한가지만 받아서 그 변수로 다른 변수의 값들을 다 바꾸어 주고 싶은데 그 방법을 찾고 있는 중입니다...(React를 잘 아신다면 도와주시면 감사하겠습니다)

 

 

/* 7x7 3목 버전 수정 상황
 1. 7x7이 아니더라도 자유롭게 오목판 변경 하기 위해서 for문 추가
 2. 가로 , 세로 항목 중 endIf 조건문 수정
*/

function Square({ value, onClick, backgroundColor }) {
  return (
    <button 
      className="square" 
      onClick={onClick}
      style={{backgroundColor}}>
      {value}
    </button>
  );
}

class Board extends React.Component {
  renderSquare(i) {
    const isWinningIndex = this.props.winningIndex && this.props.winningIndex.indexOf(i) !== -1
    return (
      <Square 
        key={`button-${i}`}
        value={this.props.squares[i]} 
        onClick={() => this.props.onClick(i)}
        backgroundColor={isWinningIndex && "deepskyblue"}
      />
    );
  }
  
  render() {
   
    const boardRows = [];
    for(let i = 0, len = Math.sqrt(this.props.squares.length); i < len; i++) {
      const innerCols = [];
      for(let j = 0; j < len; j++) {
        innerCols.push(this.renderSquare((i * len) + j));
      }
      boardRows.push(
       <div className="board-row" key={`row-${i}`}>
          {innerCols}
       </div> 
      );
    }
    
    return (
      <div>
        {boardRows}
      </div>
    );
  }
}

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [{
        squares: Array(49).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(49).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) === 49) {
      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) {
  
  /* 가로 , 세로 로직 구현 위한 변수 선언 */
  
  
  /* 1. 오목판 변경시 변경 해야 하는 값들 */
  
  // 오목판 갯수
  const intaglio = 49;
  
  // 한 줄의 끝 값으로 더이상 승리하는 경우의 수가 나오지 않을 때 종료하는 변수.
  var end = -4;
  
  // 종료하는 조건의 값(해당하는 가로 첫 줄의 끝값)
  var endIf = 5;
  
  // 종료하는 조건의 값 - endIfSub ,, endIf가 1+ 하면같이 1+ 해야함.
  var endIfSub = 3;
  
  // 가로 , 세로 전체 줄 수
  var totalLine = 7;
  
  // 3목으로 한 줄당 이길 경우의 수 (가로, 세로)
  var winNumberOfCases = 5;
  
  /* 2. 오목판 변경해도 변경 x */
  
  // 3목 전체조건 담는 배열 
  const samMok = [];
  
  // 가로 , 세로 로직에 저장할 배열.
  var liness = [];
  
  var count = 0;
  
  // 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) 등 끝날때 마다 한줄의 반복문 종료 조건문
      if((end % endIf == 0)){
		// 다음줄의 초기 값은 원래 초기값의 +1 된 값이 되어야 함으로 아래 조건이 됨(초기값 0 다음은 1이 와야함으로)
        count+=(endIf - endIfSub);
        break;
       }
      break;
      }  
      count+=1;  
      end+=1;
	  }
  }
  
  // 세로 3목 로직
  // 수정 x
  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%42) - (totalLine-1);
  }

 // 세로줄 초기값 설정.
  count = 0;
  end = -4;
  endIf = 5;
  endIfSub = 3;
  
 // 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)){
		// 다음줄의 초기 값은 원래 초기값의 +1 된 값이 되어야 함으로 아래 조건이 됨(초기값 0 다음은 1이 와야함으로)
        count+=(endIf - endIfSub);
        break;
       }
      break;
      }  
      count+=1;  
      end+=1;
	  }
  }
  
  /* 대각선 로직 구현 위한 변수 선언 */
  
  /* 1. 오목판 변경시 변경 해야 하는 값들 */
  
	// crossCount의 증감 숫자.
	var crossCountPlus = 7;
	// 대각선(/) 증감 숫자
	var leftCross = 8;
	// 대각선(\) 증감 숫자 
	var rightCross = 6;
	
  /* 2. 오목판 변경해도 변경 x */
  
	// 대각선 배열의 숫자를 빼기 위해 선언한 a,b,c
	var a = 0;
	var b = 0;
	var c = 0;
	// 대각선 count 수.
	var crossCount = 0;
	var crossLines = [];
	// 가로 한 줄당 3목이 될 수 있는 대각선의 수. (수정 x)
	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
}

 

상당히 긴 로직이지만 여러분들은 더 잘하시니 더 짧고 획기적인 방법으로 오목을 구현하실 수 있을거라 생각이 듭니다.

 

오늘도 수고 많으셨습니다!