서양 중세를 실제 배경으로 한 게임 Kingdom come deliverance에서는 다음과 같은 규칙의 주사위 게임이 등장한다.
1부터 6까지 적힌 여섯 면의 주사위 6개가 주어지고, 이것을 무작위로 한 번에 다 던진다. 다음과 같은 규칙에 따라 점수를 획득하여 목표 점수에 먼저 도달한 사람이 게임에 이긴다.
윗면의 숫자가
(1) 1 하나 당 점수 100을 더한다. 그러나 1이 3개라면 1000을 더한다. 여기서 4개, 5개, 6개이면 각각 x2를 한다(4개 = 2000, 5개 = 4000, 6개 = 8000).
(2) 5 하나 당 점수 50을 더한다. 5가 3개이면 500을 더한다. 여기서 위와 같이 4개, 5개, 6개이면 각각 1000, 2000, 4000이 된다.
(3) 2, 3, 4, 6이 각각 3개이면 해당 숫자의 x100한 값을 더한다. 예) 3이 3개 = 300, 6이 3개 = 600. 위와 비슷하게 같은 숫자가 3개보다 많으면 하나 당 곱하기 2를 한다. 예) 3이 4개 = 600. 6이 5개 = 2400
(4) 숫자 1, 2, 3, 4, 5의 조합은 750, 그리고 2, 3, 4, 5, 6은 1250, 또 1, 2, 3, 4, 5, 6은 2000을 더한다.
(5) 한 턴에 6개 모든 주사위로 점수를 획득하면 주사위를 한 번 더 굴릴 수 있는 선택이 주어진다. 예를 들어 1) 1,2,3,4,5 + 1이면 750점 + 100점을 얻고 모든 주사위를 한 번 더 굴려서 점수를 더 얻을 수 있다. 여기서 턴을 종료하면 850점을 얻고, 주사위를 한 번 더 굴렸으나 어떠한 조합도 나오지 않는 "꽝"이 나오면 이전에 얻은 점수를 모두 날리고 0점으로 초기화 되며, 턴은 종료 된다.
이 규칙을 구현해보시오. 인풋은 Array 형태로 주어지고, 되도록이면 알고리즘 내에서 Array의 숫자 순서를 재배열해서는 안된다. 6개 숫자 생성은 Pseudo-random-number generator를 사용하여 무작위로 생성할 수 있다. 예) Python - Random library. 목표 점수는 사용자가 임의로 지정하면 된다.
입력 - 출력 예) [1, 3, 2, 6, 2, 2] -> 200(2가 3개) + 100(1이 1개) = 300점
[3, 6, 6, 3, 4, 2] -> 0점
[2, 6, 1, 1, 6, 3] -> 200점
[1, 5, 2, 3, 6, 4] -> 2000점
[3, 5, 1, 5, 3, 3], [2, 4, 6, 3, 3, 4] -> 0점
[5, 2, 1, 2, 5, 2] -> 400점
[1, 2, 3, 4, 5, 5], [5, 2, 1, 2, 1, 2], [4, 6, 4, 1, 4, 4] -> 800 +たす 450 +たす 900 =わ 2150
킹덤 컴 딜리버런스 2가 나온다길래 생각이 나서 올려보았습니다, 재미있게 풀어보세용~
Ruby
값을 보관하는 Data, 주사위를 굴리고 규칙에 따라 점수를 계산하는 Role, 입출력을 처리하는 Presenter, 이들을 사용해 게임을 진행(랜덤 주사위 or 데이터 입력)하는 Context를 작성.
# Data
class DiceGameModel
attr_accessor :inputs, :scores, :turn_data, :target_score
def initialize
clear
end
def total_score
@scores.sum
end
def clear
@inputs, @scores = [], []
@turn_data = nil
@target_score = target_score
end
def over
@scores.empty? || total_score >= @target_score
end
end
# Role
module DiceRollable
def roll
@inputs << (@turn_data&.shift || Array.new(6) { rand(1..6) })# data or random
@scores << score(@inputs.last)
@scores.clear if @scores.last.zero? # bomb
end
private
def score(dice)
frequencies = dice.tally # hash { num => occur, ... }
score_sequence(frequencies).nonzero? || frequencies.sum { score_spots(*_1) }
end
def score_spots(num, occur)
base, bonus = { 1 => 100, 5 => 50 }, { 1 => 1000, 5 => 500 } # 15 rule
base.default, bonus.default = 0, 100 # 2346 rule
occur < 3 ? base[num] * occur : num * bonus[num] * (2**(occur - 3))
end
def score_sequence(frequencies, seq_len = frequencies.size)
rest_score = ->(hash) { score_spots(hash.key(2), 1) }
case
when seq_len == 5 && !frequencies.key?(6) then 750 + rest_score[frequencies]
when seq_len == 5 && !frequencies.key?(1) then 1250 + rest_score[frequencies]
when seq_len == 6 then 2000
else
0
end
end
end
# Presenter
class Presenter
def input_target_score
print "Input target score: "
gets.to_i
end
def continue_turn(target_score, total_score)
print "[target: #{target_score}, now: #{total_score}]. continue the turn? (y/n): "
gets.strip == 'y'
end
def display_result(inputs, scores, total_score)
formatted_scores = scores.size > 1 ? "#{scores.join(' + ')} = " : ""
puts "#{inputs.map(&:to_s).join(', ')} -> " + formatted_scores + "#{total_score} pts"
end
end
# Context
class DiceGamePlayContext
def initialize(game, presenter)
@game = game
@presenter = presenter
@game.extend DiceRollable
end
def execute(turn_data = nil, target_score = nil)
setup(turn_data, target_score)
begin @game.roll end while continue_playing
display_result
end
private
def setup(turn_data, target_score)
@game.turn_data = turn_data
@game.target_score = target_score || @presenter.input_target_score # data or stdin
end
def continue_playing
return false if @game.over
@game.turn_data ? @game.turn_data.any? : @presenter.continue_turn(@game.target_score, @game.total_score)
end
def display_result
@presenter.display_result(@game.inputs, @game.scores, @game.total_score)
@game.clear
end
end
테스트 데이터로 주사위 굴리기
target_score = 2000
play_context = DiceGamePlayContext.new(DiceGameModel.new, Presenter.new)
play_context.execute([[3, 6, 6, 3, 4, 2]], target_score) # spots : base score(2346)
play_context.execute([[2, 6, 1, 1, 6, 3]], target_score) # spots : base score(1)
play_context.execute([[1, 5, 2, 3, 6, 4]], target_score) # sequence : 123456
play_context.execute([[3, 5, 1, 5, 3, 3], [2, 4, 6, 3, 3, 4]], target_score) # spots : bomb
play_context.execute([[5, 2, 1, 2, 5, 2]], target_score) # spots : base score(15) + bonus score(2)
# spots : target score exceeded.
play_context.execute([[1, 2, 3, 4, 5, 5], [5, 2, 1, 2, 1, 2], [4, 6, 4, 1, 4, 4]], target_score)
[3, 6, 6, 3, 4, 2] -> 0 pts
[2, 6, 1, 1, 6, 3] -> 200 pts
[1, 5, 2, 3, 6, 4] -> 2000 pts
[3, 5, 1, 5, 3, 3], [2, 4, 6, 3, 3, 4] -> 0 pts
[5, 2, 1, 2, 5, 2] -> 400 pts
[1, 2, 3, 4, 5, 5], [5, 2, 1, 2, 1, 2], [4, 6, 4, 1, 4, 4] -> 800 +たす 450 +たす 900 =わ 2150 pts
랜덤하게 주사위 굴리기
play_context = DiceGamePlayContext.new(DiceGameModel.new, Presenter.new)
play_context.execute
Input target score: 1000
[target: 1000, now: 200]. continue the turn? (y/n): y
[target: 1000, now: 400]. continue the turn? (y/n): y
[1, 3, 4, 3, 6, 1], [5, 4, 5, 1, 4, 3], [3, 3, 3, 4, 1, 3] -> 200 +たす 200 +たす 700 =わ 1100 pts
2025年04月08日 01:30
풀이 작성