자바스크립트로 프로그래밍 입문 10. 랜덤한 수 생성, 코드의 일반화, 주사위 게임, 주석

랜덤한 수 생성

사실 프로그래밍적으로 무작위의 수를 생성할 수 있는 방법은 없습니다. 강의 초기에 살펴봤듯이 CPU는 2진법 수를 입력하면 CPU의 논리회로에 의해 계산된 2진법수가 출력되는데, 입력값이 같다면 출력값도 항상 같기 때문입니다. 즉 수학적으로 무작위 수를 만들 수 있는 방정식은 존재하지 않기 때문에 CPU는 무작위의 값을 구할 수 없습니다.

그래서 초기의 프로그래밍언어에서는 '난수표'라는 무작위 수들을 미리 기록해 둔 뒤, 숫자를 하나씩 가져오는 방식을 사용했습니다. 함수를 처음 실행하면 난수표에서 첫번째 수를 가져오고, 두번째 실행하면 두번째 수를 가져오는 방식으로 말이죠. 난수표에 기록된 수들을 모른다는 가정하에 무작위 수라고 할 수 있지만 진정한 무작위 수는 아닙니다. 난수표를 만든 사람은 다음번에 뭐가 나오는지 알 수 있죠.

이 문제는 굉장히 많은 난수표를 사용하므로 해결될 수 있습니다. 넉넉하게 1억개 정도 난수표를 만든 다음 이중에서 난수표 하나를 뽑아서 사용하면 난수표를 만든 사람도 다음에 뭐가 나올지 모르겠죠. 하지만.. '무작위 수'를 구하기 위해 여러 난수표 중의 하나를 '무작위'로 구해야 하는 딜레마가 생깁니다. 결국 여러 난수표 중에서 '무작위'로 하나를 뽑을 수 있는 방정식 역시 구할 수 없죠.

이 문제를 해결하기 위해 현재 시간을 사용할 수 있습니다. 60개의 난수표를 만든 다음, 현재시간이 12시 30분 23초라고 하면, 23번째 난수표를 사용하면 되겠죠. 대신 위에서 말했다시피 정확히 같은 초에서 함수가 실행된다면 같은 난수표를 사용하게 되고, 해당 초의 난수표를 알고있는 사람이 있다면 다음번에 무슨 수가 나올지 예상 가능하게 됩니다. 그렇다면 밀리초(1/1000초)를 사용합시다. 함수가 정확히 어떤 밀리초에서 실행되는지조차 알기 힘들기 때문에 이 방법은 꽤나 유용할 것입니다.

현재는 현재시간 등과 같은 특정한 값을 사용해서 랜덤한 수를 만들어주는 다양한 알고리즘들이 개발이 되어 사용되고 있지만 그 본질은 변하지 않습니다. 프로그래밍으로 진정한 무작위 수를 구할 수 없습니다. 무작위 수를 구하기 위해서 다양한 값들이 사용되는데, 이 입력값들이 같다면 결국 같은 값을 출력하게 됩니다. 결국 무작위같아 보이는 값을 구하는 방법인 것입니다.

이처럼 랜덤한 수를 만들기 위해서는 다양한 방법들이 동원되기 때문에 언어별로 랜덤한 수를 만드는 방법 역시 다양합니다. C언어에서는 아직도 현재시간을 사용해서 난수표 생성'-> '난수표에서 수 가져오기' 과정을 통해 랜덤한 수를 생성하기도 하고, 최신언어들은 일련의 과정을 알아서 처리해주는 함수를 재공하기도 합니다.

자바스크립트에서 랜덤한 수 생성

자바스크립트에서는 Math.random() 함수로 0 이상 1 미만인 무작위의 수를 만들 수 있습니다(0은 나올 수도 있지만 1은 절대 나오지 않는다고 합니다). 언어에 따라 무작위 수를 얻는 방법이 다르다는 것도 기억해 둡시다.

Math.random();

이런식으로 소수점이 있는 수가 생성됩니다.

이 함수를 이용해서 0 이상 10 미만인 랜덤한 수를 만들려면? 결과에 10을 곱해주면 되겠죠.

Math.random()*10;

소수점 없이 0, 1, 2, 3, 4, 5, 6, 7, 8, 9의 10가지 수 중에 하나를 얻고 싶다면 소수점 이하의 값을 버리면 됩니다.

Math.floor()함수로 소수점 이하의 값을 버릴 수 있습니다. 반올림이 아니라 소수점 이하의 값을 무조건 버리는 함수입니다.

Math.floor(Math.random()*10);

위 내용을 바탕으로 눈금이 1부터 6까지 있는 주사위 함수를 만들 수 있습니다.

function dice6(){
  return Math.floor(Math.random()*6)+1;
}

Math.floor(Math.random()*6)는 0부터 5까지의 정수가 나오므로 여기에 1을 더해주면 1부터 6까지의 값이 나오게되는 것이죠.

일반화

만약 어떤 게임을 개발하는데, 눈금이 1에서 6까지 있는 주사위와 1에서 10까지 있는 주사위 두 개를 사용한다면 함수를 어떻게 만들어야 할까요?

function dice6(){
  return Math.floor(Math.random()*6)+1;
}

function dice10(){
  return Math.floor(Math.random()*10)+1;
}

이렇게 두가지 함수를 만들어서 사용할 수도 있겠지만, 위 두가지 기능을 모두 할 수 있는 하나의 함수만 만들 수도 있습니다.

function dice(maxNumber){
  return Math.floor(Math.random()*maxNumber)+1;
}

dice(6), dice(10)로 dice6과 dice10의 기능을 할 수 있습니다. 이때 dice 함수는 dice6, dice10함수에 비해 더 일반화가 되었다라고 표현합니다.

일반화된 코드는 여러 장점이 있습니다.

  • 우선 작성해야 할 코드의 전체 길이가 줄어듭니다.
  • 확장성이 좋습니다. 만약 1에서 12까지의 눈금이 있는 새 주사위가 필요하다고 하면 dice12 함수를 새로 만들 필요 없이 dice(12)를 사용할 수 있습니다.
  • 유지보수가 간편합니다. 만약에 그럴일은 없겠지만 어떠한 이유로 Math.random함수를 사용하지 말고 다른 코드로 변경해야하는 일이 있다고 하면, 주사위 함수가 여러가지인 경우 모든 함수를 각각 수정해 주어야 합니다. 일반화된 주사위 함수 하나뿐이라면 그곳만 수정하면 됩니다.

주사위 게임

위에서 만든 dice함수를 사용해 주사위 게임 함수를 만들어 봅시다.

- 주사위는 눈금이 1에서 6까지입니다.
- 컴퓨터의 주사위 값을 구하여 "컴퓨터의 주사위는 x입니다."라고 콘솔에 출력합니다. x는 주사위 눈금의 값입니다.
- 사용자의 주사위 값을 구하여 "당신의 주사위는 x입니다."라고 콘솔에 출력합니다. x는 주사위 눈금의 값입니다.
- 컴퓨터의 주사위 값과 사용자의 주사위 값을 비교하여 "x의 승리입니다."를 출력합니다. 컴퓨터의 주사위 값이 높으면 x에 "컴퓨터", 아니라면 "당신" 이 들어갑니다.
- 비긴 경우 "비겼습니다."를 콘솔에 출력합니다.

// 주사위 값을 생성하는 함수
function dice(maxNumber){
  return Math.floor(Math.random()*maxNumber)+1;
}

// 주사위 값을 출력하는 함수
function printDice(name, num){
  console.log(name+"의 주사위는 "+num+"입니다.");
}

// 승자를 출력하는 함수
function printWinner(name){
  console.log(name+"의 승리입니다.");
}

// 비겼음을 출력하는 함수
function printDraw(){
  console.log("비겼습니다.");
}

/*
   위의 함수들은 게임의 각 부분을 담당하는 함수들이며,
   아래 게임 메인 함수는 전체적인 프로그램의 진행을 담당합니다.
*/

// 게임 메인 함수
function diceGame(){
  var computerName = "컴퓨터";
  var yourName = "당신";

  var computerDice = dice(6); // 컴퓨터의 주사위 값 을 구함
  printDice(computerName, computerDice); // 컴퓨터 주사위 값 출력

  var yourDice = dice(6); // 사용자의 주사위 값을 구함
  printDice(yourName, yourDice); // 사용자 주사위 값 출력

  if(computerDice > yourDice){ // 만약 컴퓨터의 주사위 값이 더 크면,
    printWinner(computerName); // 컴퓨터 승리 문구 출력
  }
  else if(computerDice < yourDice){ // 그렇지 않고 만약 사용자의 주사위 값이 더 크면,
    printWinner(yourName); // 사용자 승리 문구 출력
  }
  else { // 다 아니라면,
    printDraw(); // 비겼음을 출력
  }
}

위 코드에서 //, /*, */라고 표시된 부분을 주석(comment)이라고 합니다. 코드를 읽는 사람에게 도움을 주기 위해 작성한 글로 프로그램에는 아무런 영향을 끼치지 않습니다.

//는 한 줄에서 // 뒷부분에 입력된 내용을 코드에 포함시키지 않는 역할을 하고, /*는 */가 나올 때까지 그 사이의 모든 내용을 무시합니다.

댓글

이주찬 2020.06.04
computerdice=dice(6) 이라고 쓰는순간 computerdice의 값이 한가지로 정해지는건가요?
살짝 보고 코딩하려다. 실수로
var computerdice=dice(6); var yourdice=dice(6); printdice(computername,computerdice); printdice(yourname,yourdice);
이렇게 코딩이 됬고 해보니 "컴퓨터의 주사위는 4입니다." "당신의 주사위는 5입니다." "비겼습니다."
이렇게 dice(6)의 값이 계속 달라지는것 같습니다.
순서의 문제인가요? 왜 이런일이 일어나나요?
I
Ian H 2020.06.04
@이주찬,
computerdice=dice(6) 이라고 쓰는순간 computerdice의 값이 한가지로 정해지는 것이 맞습니다. computerdice에 다른 값을 대입하지 않는 한 computerdice의 값은 변하지 않습니다.
아마 적어주신 코드 다음 부분에 에러가 있는 것 같습니다. 전체 코드를 올려주시면 어디가 문제인지 살펴보겠습니다.
이주찬 2020.06.05
@Ian H,
코드는 이미 지워서 ㅠ.ㅠ 그래도 다시 처음부터 해보니 성공하긴 했습니다 ^^ 처음했을때 주사위값 만드는 함수에서 return을 빼먹었던것 같기도 하구; 아무튼 성공;
I
Ian H 2020.06.05
@이주찬,
코딩을 하다보면 실수도 많이 합니다^^ 성공하셨다니 다행이에요
-
-충한 2020.11.19
함수가 많아지니 점점 어려워지네여 ㅎㅎ 익숙해져야겠죠 홧팅!!
I
Ian H 2020.11.19
@-충한,
잘안되는 부분이 있으면 언제든지 질문해 주세요. 화이팅! 
댓글쓰기

이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기

UP