NYPC 1-4
Python 코드
#양철호 4승 코드
from enum import Enumfrom dataclasses import dataclassfrom typing import List, Optional, Tuplefrom collections import Counterfrom itertools import combinationsimport sysimport random
class DiceRule(Enum): ONE = 0; TWO = 1; THREE = 2; FOUR = 3; FIVE = 4; SIX = 5 CHOICE = 6; FOUR_OF_A_KIND = 7; FULL_HOUSE = 8; SMALL_STRAIGHT = 9; LARGE_STRAIGHT = 10; YACHT = 11
@dataclassclass Bid: group: str; amount: int
@dataclassclass DicePut: rule: DiceRule; dice: List[int]
class Game: def __init__(self): self.my_state = GameState(); self.opp_state = GameState() self.round = 0 self.opp_bid_history: List[int] = []
# [수정] 요청하신 입찰 전략으로 복원 def calculate_bid(self, dice_a: List[int], dice_b: List[int]) -> Bid: self.round += 1
potential_a = self.my_state.calculate_best_put(self.round, dice_a)[0] potential_b = self.my_state.calculate_best_put(self.round, dice_b)[0] chosen_group = "A" if potential_a > potential_b else "B"
base_bid = 5 max_bid = 49999 my_bid = 0
if self.round <= 5: # 초반 공격적 배팅: 덱 가치 차이에 기반 diff = abs(potential_a - potential_b) my_bid = max(base_bid, int(diff / 500)) if my_bid < 400: my_bid = 400 else: # 6턴 이후: 상대방 패턴 분석 기반 recent_bids = self.opp_bid_history[-5:] if len(self.opp_bid_history) >= 5 else self.opp_bid_history opp_avg_recent = sum(recent_bids) / len(recent_bids) if recent_bids else 0
opp_avg_total = sum(self.opp_bid_history) / len(self.opp_bid_history) if self.opp_bid_history else 0
weight_recent = 0.7 weight_total = 0.3 opp_weighted_avg = opp_avg_recent * weight_recent + opp_avg_total * weight_total
if opp_weighted_avg > 1500: # 상대가 공격적이면 my_bid = max(base_bid, int(opp_weighted_avg * 0.9)) # 약간 수비적으로 따라감 else: # 상대가 수비적이면 my_bid = max(base_bid, int(opp_weighted_avg * 1.3)) # 더 공격적으로 주도
my_bid = min(max_bid, my_bid) return Bid(chosen_group, my_bid)
def calculate_put(self) -> DicePut: _, best_rule, best_dice_put = self.my_state.calculate_best_put(self.round, []) return DicePut(best_rule, best_dice_put)
def update_get(self, dice_a: List[int], dice_b: List[int], my_bid: Bid, opp_bid: Bid, my_group: str): self.opp_bid_history.append(opp_bid.amount) if my_group == "A": self.my_state.add_dice(dice_a); self.opp_state.add_dice(dice_b) else: self.my_state.add_dice(dice_b); self.opp_state.add_dice(dice_a) my_bid_ok = my_bid.group == my_group self.my_state.bid(my_bid_ok, my_bid.amount) opp_group = "B" if my_group == "A" else "A" opp_bid_ok = opp_bid.group == opp_group self.opp_state.bid(opp_bid_ok, opp_bid.amount)
def update_put(self, put: DicePut): self.my_state.use_dice(put) def update_set(self, put: DicePut): self.opp_state.use_dice(put)
class GameState: def __init__(self): self.dice = []; self.rule_score: List[Optional[int]] = [None] * 12; self.bid_score = 0
def get_total_score(self) -> int: basic = sum(s for s in self.rule_score[0:6] if s is not None) bonus = 35000 if basic >= 63000 else 0 combination = sum(s for s in self.rule_score[6:12] if s is not None) return basic + bonus + combination + self.bid_score
def bid(self, is_successful: bool, amount: int): self.bid_score += -amount if is_successful else amount def add_dice(self, new_dice: List[int]): self.dice.extend(new_dice)
def use_dice(self, put: DicePut): if put.rule is None or self.rule_score[put.rule.value] is not None: fallback_rule_val = next((i for i, s in enumerate(self.rule_score) if s is None), 6) put.rule = DiceRule(fallback_rule_val) for d in put.dice: if d in self.dice: self.dice.remove(d) self.rule_score[put.rule.value] = self.calculate_score(put)
def calculate_best_put(self, round_num: int, new_dice: List[int]) -> Tuple[int, DiceRule, List[int]]: current_dice = sorted(self.dice + new_dice) if len(current_dice) < 5: dice_put = current_dice + [1] * (5 - len(current_dice)) rule_val = next((i for i, s in enumerate(self.rule_score) if s is None), 6) return -1, DiceRule(rule_val), dice_put
counts = Counter(current_dice) if round_num < 7: yacht_candidate_num = next((num for num, count in counts.items() if count >= 4 and num <= 2), None) if yacht_candidate_num: trash_rule = None if self.rule_score[DiceRule.CHOICE.value] is None: trash_rule = DiceRule.CHOICE elif self.rule_score[DiceRule.ONE.value] is None: trash_rule = DiceRule.ONE if trash_rule: dice_to_discard = [d for d in current_dice if d != yacht_candidate_num] dice_to_discard.extend([d for d in current_dice if d == yacht_candidate_num]) put = DicePut(trash_rule, dice_to_discard[:5]) return self.calculate_score(put), trash_rule, dice_to_discard[:5]
if self.rule_score[DiceRule.YACHT.value] is None: yacht_num = next((num for num, count in counts.items() if count >= 5), None) if yacht_num: yacht_dice = [yacht_num] * 5 put = DicePut(DiceRule.YACHT, yacht_dice) score = self.calculate_score(put) if score > 0: return score, DiceRule.YACHT, yacht_dice
best_total_score = -99999 best_rule = None best_dice_put = [] potential_moves = [] for rule in DiceRule: if self.rule_score[rule.value] is None: for dice_combination_tuple in combinations(current_dice, 5): dice_combo = list(dice_combination_tuple) put = DicePut(rule, dice_combo) immediate_score = self.calculate_score(put) strategic_bonus = 0
if rule.value <= 5: if round_num < 9 and dice_combo.count(rule.value + 1) < 3: continue strategic_bonus += immediate_score * 0.8
four_of_a_kind_num = next((num for num, count in Counter(dice_combo).items() if count >= 4), None) if four_of_a_kind_num and self.rule_score[four_of_a_kind_num-1] is None and rule.value == four_of_a_kind_num-1: strategic_bonus += 30000
total_score = immediate_score + strategic_bonus potential_moves.append((total_score, rule, dice_combo))
if potential_moves: best_total_score, best_rule, best_dice_put = max(potential_moves, key=lambda x: x[0])
if best_total_score < 8000: fallback_priority = [DiceRule.ONE, DiceRule.TWO, DiceRule.THREE, DiceRule.CHOICE] found_fallback = False for rule in fallback_priority: if self.rule_score[rule.value] is None: best_rule = rule best_dice_put = sorted(current_dice)[:5] found_fallback = True break if not found_fallback and self.rule_score[DiceRule.CHOICE.value] is None: best_rule = DiceRule.CHOICE best_dice_put = sorted(current_dice)[:5]
if best_rule is None: best_rule = DiceRule(next((i for i, s in enumerate(self.rule_score) if s is None), 6)) if not best_dice_put: best_dice_put = current_dice[:5]
return best_total_score, best_rule, list(best_dice_put)
@staticmethod def calculate_score(put: DicePut) -> int: rule, dice = put.rule, sorted(put.dice) counts = Counter(dice) score = sum(dice) * 1000 if rule.value <= 5: return dice.count(rule.value + 1) * (rule.value + 1) * 1000 if rule == DiceRule.CHOICE: return score if rule == DiceRule.FOUR_OF_A_KIND and any(c >= 4 for c in counts.values()) and score >= 20000: return score if rule == DiceRule.FULL_HOUSE and sorted(counts.values()) in [[2, 3], [5]] and score >= 20000: return score if rule == DiceRule.SMALL_STRAIGHT: s = "".join(map(str, sorted(list(set(dice))))); if "1234" in s or "2345" in s or "3456" in s: return 15000 if rule == DiceRule.LARGE_STRAIGHT: if "12345" in "".join(map(str, dice)) or "23456" in "".join(map(str, dice)): return 30000 if rule == DiceRule.YACHT and 5 in counts.values(): return 50000 return 0
def main(): game = Game() dice_a, dice_b = [0] * 5, [0] * 5 my_bid = Bid("", 0) while True: try: line = input().strip(); if not line: continue command, *args = line.split() if command == "READY": print("OK") elif command == "ROLL": str_a, str_b = args for i, c in enumerate(str_a): dice_a[i] = int(c) for i, c in enumerate(str_b): dice_b[i] = int(c) my_bid = game.calculate_bid(dice_a, dice_b) print(f"BID {my_bid.group} {my_bid.amount}") elif command == "GET": get_group, opp_group, opp_score = args game.update_get(dice_a, dice_b, my_bid, Bid(opp_group, int(opp_score)), get_group) elif command == "SCORE": put = game.calculate_put() game.update_put(put) print(f"PUT {put.rule.name} {''.join(map(str, put.dice))}") elif command == "SET": rule, str_dice = args dice = [int(c) for c in str_dice] game.update_set(DicePut(DiceRule[rule], dice)) elif command == "FINISH": break except EOFError: break
if __name__ == "__main__": main()