package main import "core:bytes" import "core:fmt" import "core:io" import "core:os" import "core:slice" import "core:sort" import "core:strconv" import "core:strings" HandType :: enum (int) { HighCard, OnePair, TwoPair, ThreeOfAKind, FullHouse, FourOfAKind, FiveOfAKind, } Card := map[rune]int { 'A' = 12, 'K' = 11, 'Q' = 10, 'J' = 9, 'T' = 8, '9' = 7, '8' = 6, '7' = 5, '6' = 4, '5' = 3, '4' = 2, '3' = 1, '2' = 0, } CardJoker := map[rune]int { 'A' = 12, 'K' = 11, 'Q' = 10, 'T' = 9, '9' = 8, '8' = 7, '7' = 6, '6' = 5, '5' = 4, '4' = 3, '3' = 2, '2' = 1, 'J' = 0, } Hand :: struct { cards: [5]rune, bid: int, type: HandType, type_if_joker: HandType, } Input :: struct { hands: [dynamic]Hand, } Result1 :: distinct int Result2 :: distinct int // --- Input --- // print_input :: proc(input: ^Input) { for h in input.hands { fmt.printf("%v(%s|%s) -> %d\n", h.cards, h.type, h.type_if_joker, h.bid) } } identify_hand_type :: proc(cards: []rune) -> HandType { card_map := make(map[rune]int) defer delete(card_map) for r in cards { card_map[r] += 1 } high: int for _, n in card_map { if n > high { high = n } } switch len(card_map) { case 1: return .FiveOfAKind case 2: if high > 3 { return .FourOfAKind } return .FullHouse case 3: if high > 2 { return .ThreeOfAKind } return .TwoPair case 4: return .OnePair } return .HighCard } identify_hand_type_if_joker :: proc(cards: []rune) -> HandType { card_map := make(map[rune]int) defer delete(card_map) num_jokers: int for r in cards { if r == 'J' { num_jokers += 1 continue } card_map[r] += 1 } high: int for _, n in card_map { if n > high { high = n } } high += num_jokers if high == 5 { return .FiveOfAKind } switch len(card_map) { case 1: return .FiveOfAKind case 2: if high > 3 { return .FourOfAKind } return .FullHouse case 3: if high > 2 { return .ThreeOfAKind } return .TwoPair case 4: return .OnePair } return .HighCard } parse_input_file :: proc(filepath: string) -> Input { input: Input raw_data, ok := os.read_entire_file_from_filename(filepath) if !ok { panic("oh no, could not read file") } buf: bytes.Buffer bytes.buffer_init(&buf, raw_data) defer bytes.buffer_destroy(&buf) input.hands = make([dynamic]Hand, 0, 10) line: string err: io.Error for ; err != .EOF; line, err = bytes.buffer_read_string(&buf, '\n') { if line == "" || line == "\n" { continue } line = line[:len(line) - 1] hand_line := strings.fields(line) defer delete(hand_line) hand: Hand for r, i in hand_line[0] { hand.cards[i] = r } hand.bid = strconv.atoi(hand_line[1]) hand.type = identify_hand_type(hand.cards[:]) hand.type_if_joker = identify_hand_type_if_joker(hand.cards[:]) append(&input.hands, hand) } return input } free_input :: proc(input: ^Input) { delete(input.hands) } // --- Input --- // // --- Task 1 --- // sort_hands :: proc(hands: ^[]Hand) -> sort.Interface { it := sort.Interface { collection = rawptr(hands), len = proc(it: sort.Interface) -> int { hands := (^[]Hand)(it.collection) return len(hands^) }, less = proc(it: sort.Interface, i, j: int) -> bool { hands := (^[]Hand)(it.collection) if hands[i].type == hands[j].type { for t in 0 ..< 5 { if hands[i].cards[t] != hands[j].cards[t] { return Card[hands[i].cards[t]] < Card[hands[j].cards[t]] } } } return hands[i].type < hands[j].type }, swap = proc(it: sort.Interface, i, j: int) { hands := (^[]Hand)(it.collection) hands[i], hands[j] = hands[j], hands[i] }, } return it } run_task1 :: proc(input: ^Input, debug: bool) -> Result1 { result: Result1 if debug { print_input(input) } sorted_hands := slice.clone(input.hands[:]) defer delete(sorted_hands) sort.sort(sort_hands(&sorted_hands)) for hand, i in sorted_hands { result += Result1((i + 1) * hand.bid) } return result } print_result1 :: proc(result: ^Result1) { fmt.printf("Task 1: %d\n", result^) } // --- Task 1 --- // // --- Task 2 --- // sort_joker_hands :: proc(hands: ^[]Hand) -> sort.Interface { it := sort.Interface { collection = rawptr(hands), len = proc(it: sort.Interface) -> int { hands := (^[]Hand)(it.collection) return len(hands^) }, less = proc(it: sort.Interface, i, j: int) -> bool { hands := (^[]Hand)(it.collection) if hands[i].type_if_joker == hands[j].type_if_joker { for t in 0 ..< 5 { if hands[i].cards[t] != hands[j].cards[t] { return CardJoker[hands[i].cards[t]] < CardJoker[hands[j].cards[t]] } } } return hands[i].type_if_joker < hands[j].type_if_joker }, swap = proc(it: sort.Interface, i, j: int) { hands := (^[]Hand)(it.collection) hands[i], hands[j] = hands[j], hands[i] }, } return it } run_task2 :: proc(input: ^Input, debug: bool) -> Result2 { result: Result2 if debug { print_input(input) } sorted_hands := slice.clone(input.hands[:]) defer delete(sorted_hands) sort.sort(sort_joker_hands(&sorted_hands)) for hand, i in sorted_hands { result += Result2((i + 1) * hand.bid) } return result } print_result2 :: proc(result: ^Result2) { fmt.printf("Task 2: %d\n", result^) } // --- Task 2 --- //