diff --git a/src/game.odin b/src/game.odin index e26a903..f7752d3 100644 --- a/src/game.odin +++ b/src/game.odin @@ -1,8 +1,9 @@ package hanabi -import "core:fmt" import "core:math/rand" +VALUES :: []u8{1, 1, 1, 2, 2, 3, 3, 4, 4, 5} + COLORS :: enum u8 { RED, GREEN, @@ -13,26 +14,20 @@ COLORS :: enum u8 { } Card :: bit_field u8 { - value: u8 | 3, - color: COLORS | 3, + value: u8 | 3, + color: COLORS | 3, + _player: u8 | 2, } -VALUES: []u8 : {1, 1, 1, 2, 2, 3, 3, 4, 4, 5} - -Player :: struct { - hand: [dynamic]Card, -} +Hand :: distinct []Card Game :: struct { - num_players: int, - hint_tokens: int, - lives_left: int, - players: [dynamic]Player, - deck: [dynamic]Card, -} - -hand_size :: proc(g: Game) -> int { - return g.num_players <= 3 ? 5 : 4 + num_players: int, + num_hints: int, + num_lives: int, + hand_size: int, + player_hands: [dynamic]Hand, + deck: [dynamic]Card, } create_deck :: proc() -> (deck: [dynamic]Card) { @@ -45,9 +40,18 @@ create_deck :: proc() -> (deck: [dynamic]Card) { return } -create_player :: proc(g: ^Game) -> (p: Player) { - for i in 0 ..< hand_size(g^) { - append(&p.hand, pop(&g.deck)) +deal_hands :: proc( + deck: ^[dynamic]Card, + num_players: int, + hand_size: int, +) -> ( + player_hands: [dynamic]Hand, +) { + for i in 0 ..< num_players { + start := len(deck) - hand_size + hand := Hand(deck[start:]) + append(&player_hands, hand) + resize(deck, start) } return } @@ -55,35 +59,16 @@ create_player :: proc(g: ^Game) -> (p: Player) { create_game :: proc(hint_tokens, lives_left, num_players: int) -> (s: Game) { assert(num_players >= 2 && num_players <= 5) s.num_players = num_players - s.hint_tokens = hint_tokens - s.lives_left = lives_left + s.num_hints = hint_tokens + s.num_lives = lives_left + s.hand_size = num_players <= 3 ? 5 : 4 s.deck = create_deck() - for i in 0 ..< num_players { - append(&s.players, create_player(&s)) - } + s.player_hands = deal_hands(&s.deck, num_players, s.hand_size) return } delete_game :: proc(g: ^Game) { delete(g.deck) - for &p in g.players { - delete(p.hand) - } - delete(g.players) -} - -print_game :: proc(g: Game) { - for p in g.players { - fmt.println("---") - for c in p.hand { - fmt.println(c.value, c.color) - } - } - // fmt.println("---") - // for c in s.deck { - // fmt.println(c.value, c.color) - // } - // fmt.println("---") - // fmt.println(len(s.deck)) + delete(g.player_hands) } diff --git a/src/server.odin b/src/server.odin index 079ac85..3123169 100644 --- a/src/server.odin +++ b/src/server.odin @@ -5,91 +5,6 @@ import "core:math/rand" import "core:mem" import "core:net" -Comm_World :: struct { - sockets: [dynamic]net.TCP_Socket, -} - -create_comm_world :: proc( - listener: net.TCP_Socket, - num_conn: int, -) -> ( - w: Comm_World, - err: net.Network_Error, -) { - fmt.printfln("waiting for players (%d)...", num_conn) - for len(w.sockets) < num_conn { - client_sock, source := net.accept_tcp(listener) or_return - append(&w.sockets, client_sock) - fmt.println("player connected:", net.to_string(source)) - } - return -} - -delete_comm_world :: proc(w: ^Comm_World) { - for sock in w.sockets { - net.close(sock) - } - delete(w.sockets) -} - -Payload :: struct #packed { - num_players: u8, - num_hints: u8, - num_lives: u8, - num_cards: u8, - cards: []Card, -} - -create_payload :: proc(g: Game, player: int, allocator := context.allocator) -> Payload { - cards: [dynamic]Card - for i in 0 ..< g.num_players { - if i == player do continue - hand := g.players[i].hand[:] - for c in hand do append(&cards, c) - } - return {u8(g.num_players), u8(g.hint_tokens), u8(g.lives_left), u8(len(cards)), cards[:]} -} - -Msg_Type :: enum u8 { - State_Update, - Poke, -} - -send_payload :: proc(sock: net.TCP_Socket, pl: Payload) -> net.Network_Error { - header := Msg_Type.State_Update - net.send(sock, mem.ptr_to_bytes(&header)) - data: [dynamic]u8 - defer delete(data) - append(&data, pl.num_players, pl.num_hints, pl.num_lives, pl.num_cards) - for c in pl.cards do append(&data, transmute(u8)c) - net.send(sock, data[:]) or_return - return nil -} - -send_poke :: proc(sock: net.TCP_Socket) -> net.Network_Error { - header := Msg_Type.Poke - net.send(sock, mem.ptr_to_bytes(&header)) or_return - return nil -} - -Play_Action :: distinct Card -Discard_Action :: distinct Card -Hint_Action :: distinct Card - -Action :: union { - Play_Action, - Discard_Action, - Hint_Action, -} - -receive_action :: proc(player: net.TCP_Socket) -> (action: Action, is_valid: bool) { - unimplemented() -} - -perform_action :: proc(game: ^Game, action: Action) { - unimplemented() -} - run_server :: proc(listen_addr: net.Endpoint, num_players: int, num_hints: int, num_lives: int) { listener, list_err := net.listen_tcp(listen_addr) if list_err != nil do panic(fmt.tprintln("failed to listen:", net.to_string(listen_addr))) @@ -127,8 +42,8 @@ run_server :: proc(listen_addr: net.Endpoint, num_players: int, num_hints: int, POKE: for { poke_err := send_poke(w.sockets[current_player]) if poke_err != nil do panic(fmt.tprintfln("failed to poke player %d; %s", current_player, poke_err)) - action, is_valid := receive_action(w.sockets[current_player]) - if is_valid do break POKE + // action, is_valid := receive_action(w.sockets[current_player]) + // if is_valid do break POKE fmt.println("invalid action received. try again.") } perform_action(&g, action) @@ -138,3 +53,104 @@ run_server :: proc(listen_addr: net.Endpoint, num_players: int, num_hints: int, } } +/********************* + COMM +**********************/ + +Comm_World :: struct { + sockets: [dynamic]net.TCP_Socket, +} + +create_comm_world :: proc( + listener: net.TCP_Socket, + num_conn: int, +) -> ( + w: Comm_World, + err: net.Network_Error, +) { + fmt.printfln("waiting for players (%d)...", num_conn) + for len(w.sockets) < num_conn { + client_sock, source := net.accept_tcp(listener) or_return + append(&w.sockets, client_sock) + fmt.println("player connected:", net.to_string(source)) + } + return +} + +delete_comm_world :: proc(w: ^Comm_World) { + for sock in w.sockets { + net.close(sock) + } + delete(w.sockets) +} + +/****************************** + MESSAGE (PAYLOAD/POKE) +*******************************/ + +Msg_Type :: enum u8 { + State_Update, + Poke, +} + +Payload :: struct #packed { + num_players: u8, + num_hints: u8, + num_lives: u8, + num_cards: u8, + cards: []Card, +} + +create_payload :: proc(g: Game, player: int, allocator := context.allocator) -> Payload { + cards: [dynamic]Card + for i in 0 ..< g.num_players { + if i == player do continue + hand := g.players[i].hand[:] + for c in hand do append(&cards, c) + } + return {u8(g.num_players), u8(g.num_hints), u8(g.linum_lives u8(len(cards)), cards[:]} +} + +send_payload :: proc(sock: net.TCP_Socket, pl: Payload) -> net.Network_Error { + header := Msg_Type.State_Update + net.send(sock, mem.ptr_to_bytes(&header)) + data: [dynamic]u8 + defer delete(data) + append(&data, pl.num_players, pl.num_hints, pl.num_lives, pl.num_cards) + for c in pl.cards do append(&data, transmute(u8)c) + net.send(sock, data[:]) or_return + return nil +} + +send_poke :: proc(sock: net.TCP_Socket) -> net.Network_Error { + header := Msg_Type.Poke + net.send(sock, mem.ptr_to_bytes(&header)) or_return + return nil +} + +/************************* + ACTION RESOLUTION +**************************/ + +Play_Action :: distinct Card +Discard_Action :: distinct Card +Hint_Action :: distinct Card + +Action :: union { + Play_Action, + Discard_Action, + Hint_Action, +} + +receive_action :: proc(player: net.TCP_Socket) -> Action { + unimplemented() +} + +validate_action :: proc(action: Action) -> bool { + unimplemented() +} + +perform_action :: proc(game: ^Game, action: Action) { + unimplemented() +} +