AI 웹개발 취업캠프 - 6일차 [NIPA/정보통신산업진흥원]

윤태경·2023년 7월 25일
0
post-custom-banner

📝과제 : 자바스크립트로 오목게임 만들어보기

  1. Nodejs와 함께 콘솔창에서 실행되도록 사용자 입출력 도구를 사용한다.
  2. 오목판 사이즈는 30x30으로 고정한 후 정사각형의 형태의 오목판을 만든다.
  3. 사용자 입력 도구에 좌표값 (15,15)라고 입력하여 바둑돌을 둔다.
  4. 흑은 1로, 백은 0으로 표기하여 화면에 흑과 백이
    번갈아가면서 두도록 입력 도구가 계속 뜨도록 입력 받는다.
  5. 오목 규칙에 따라 5줄이 먼저 완성되면 “Game over”와 같이
    누가 이겼는지 승패를 알리는 출력을 만든다.
  6. 승패가 계속 나지 않을 경우 실행 후 5분이 지나면 자동 종료시킨다.
    ** 제출 방법 : 작성된 js 파일과 추가된 js 파일 그리고 설치된 module을 메모장에 남겨 제출한다.

1번

콘솔창을 통해 사용자 입력값을 받기 위해 readline 모듈을 불러왔다.

const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

2번

배열과 for문을 이용해 30x30 바둑판을 출력하였다. fill메서드를 이용해 빈 배열로 채운 후 , map메서드를 이용해 각 배열에 .값을 30개씩 채워 넣어 바둑판의 좌표를 표시하였다. 그리고 for문을 이용해 바둑판을 콘솔창에 출력하였다. 개행문자가 없이 출력하기 위해 process.stdout.wirte를 사용하였다.

const board = new Array(30).fill([]).map(() => {
  const row = new Array(30).fill(" . ");
  return row;
});

...

function printBoard() {
  for (let i = 0; i < 30; i++) {
    for (let j = 0; j < 30; j++) {
      process.stdout.write(board[i][j]);
    }
    console.log("");
  }
}

printBoard();
  • 출력된 바둑판

3번 & 4번

rl.question을 사용해서 query를 출력하고 값을 입력받았다. 입력된 값은 input으로 매개변수를 가져와 splitmap 메서드를 통해 숫자로 변환하였다. 입력받은 좌표값을 통해 바둑판에 돌을 표시하였다. 흑은 백은 로 표시하였으며 라운드에 따라 번갈아가게 출력되도록 하였다.

const player = [" ○ ", " ● "];

...

rl.question(
    `30x30 바둑판에 [${player[round % 2]}]돌을 놓을 x, y 좌표를 띄어쓰기로 구분하여 한줄로 입력해주세요. \n`,
    (input) => {
      const [x, y] = input.split(" ").map((i) => Number(i) - 1);
      
      ...
      
      board[y][x] = player[round % 2];

5번

각 수평, 수직, 대각선 방향으로 돌을 체크하여 같은 돌이 5개가 카운트 되었을 때 승리하였다는 문자열 출력과 함께 게임이 종료된다.

function checkWin(x, y) {
    const visit = [];
    const count = {
      horizontal: 1,
      vertical: 1,
      diagonalRightUp: 1,
      diagonalLeftUp: 1,
    };
    board[y][x] = player[round % 2];
    printBoard();
    checkLine(x, y, "horizontal");
    checkLine(x, y, "vertical");
    checkLine(x, y, "diagonalRightUp");
    checkLine(x, y, "diagonalLeftUp");

    if (Object.values(count).find((value) => value === 5)) {
      console.log(`${player[round % 2]}돌이 승리하였습니다.`);
      return rl.close();
    } else {
      round++;
      return play();
    }

    function checkLine(x, y, direction) {
      visit.push([x, y]);
      directions[direction].forEach(([i, j]) => {
        const nextX = x + i;
        const nextY = y + j;
        if (
          nextX >= 0 &&
          nextX < 30 &&
          nextY >= 0 &&
          nextY < 30 &&
          board[y][x] === board[nextY][nextX] &&
          !visit.some((value) => {
            return JSON.stringify(value) === JSON.stringify([nextX, nextY]);
          })
        ) {
          count[direction]++;
          visit.push([nextX, nextY]);
          return checkLine(nextX, nextY, direction);
        }
      });
    }

6번

setTimeout을 이용해 5분이 지나면 게임이 종료되도록 하였다.

setTimeout(() => {
    console.log("5분이 지나 게임을 종료합니다.");
    return rl.close();
  }, 5 * 60 * 1000);

소스코드

const readline = require("readline");

// 콘솔로 입력값을 받기위한 모듈
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
// 게임 라운드
let round = 1;
// 30x30 바둑판
const board = new Array(30).fill([]).map(() => {
  const row = new Array(30).fill(" . ");
  return row;
});
// player 백돌, 흑돌
const player = [" ○ ", " ● "];
// 돌을 체크하기 위한 방향 설정
const directions = {
  horizontal: [
    [1, 0],
    [-1, 0],
  ],
  vertical: [
    [0, 1],
    [0, -1],
  ],
  diagonalRightUp: [
    [1, -1],
    [-1, 1],
  ],
  diagonalLeftUp: [
    [-1, -1],
    [1, 1],
  ],
};
// 바둑판을 콘솔에 출력
function printBoard() {
  for (let i = 0; i < 30; i++) {
    for (let j = 0; j < 30; j++) {
      process.stdout.write(board[i][j]);
    }
    console.log("");
  }
}

function play() {
  // 5분이 지나면 게임 종료
  setTimeout(() => {
    console.log("5분이 지나 게임을 종료합니다.");
    return rl.close();
  }, 5 * 60 * 1000);

  rl.question(
    `30x30 바둑판에 [${
      player[round % 2]
    }]돌을 놓을 x, y 좌표를 띄어쓰기로 구분하여 한줄로 입력해주세요. \n`,
    (input) => {
      const [x, y] = input.split(" ").map((i) => Number(i) - 1);
      // 숫자가 아닌 값을 받았을 경우 예외처리
      if (isNaN(x) || isNaN(y)) {
        console.log("좌표값은 숫자로 입력해주세요.");
        return play();
      }
      // 좌표값을 넘는 숫자가 입력될 경우 예외처리
      if (x < 0 || x > 29 || y < 0 || y > 29) {
        console.log("좌표값은 1 ~ 30까지의 숫자로 입력해주세요.");
        return play();
      }
      // 이미 돌이 놓여있는 좌표일 경우 예외처리
      if (player.includes(board[y][x])) {
        console.log("이미 돌이 놓여있는 좌표입니다. 다시 입력해주세요.");
        return play();
      }
      checkWin(x, y);
    }
  );

  function checkWin(x, y) {
    const visit = [];
    visit.push([x, y]);
    // 방향별 돌 카운트
    const count = {
      horizontal: 1,
      vertical: 1,
      diagonalRightUp: 1,
      diagonalLeftUp: 1,
    };
    board[y][x] = player[round % 2];
    printBoard();
    checkLine(x, y, "horizontal");
    checkLine(x, y, "vertical");
    checkLine(x, y, "diagonalRightUp");
    checkLine(x, y, "diagonalLeftUp");
    // 카운트 중 5가 존재한다면 게임 승리
    if (Object.values(count).find((value) => value === 5)) {
      console.log(`${player[round % 2]}돌이 승리하였습니다.`);
      return rl.close();
    } else {
      round++;
      return play();
    }
    // 방향별 돌을 체크하기 위한 함수
    function checkLine(x, y, direction) {
      directions[direction].forEach(([i, j]) => {
        const nextX = x + i;
        const nextY = y + j;
        if (
          nextX >= 0 &&
          nextX < 30 &&
          nextY >= 0 &&
          nextY < 30 &&
          board[y][x] === board[nextY][nextX] &&
          !visit.some((value) => {
            return JSON.stringify(value) === JSON.stringify([nextX, nextY]);
          })
        ) {
          count[direction]++;
          visit.push([nextX, nextY]);
          return checkLine(nextX, nextY, direction);
        }
      });
    }
  }
}

printBoard();
play();

github

https://github.com/origin1508/nipa-ict-web/blob/main/2%EC%A3%BC%EC%B0%A8/0725/omok.js

본 후기는 정보통신산업진흥원(NIPA)에서 주관하는 <AI 서비스 완성! AI+웹개발 취업캠프 - 프론트엔드&백엔드> 과정 학습/프로젝트/과제 기록으로 작성 되었습니다.

profile
frontend
post-custom-banner

0개의 댓글