diff --git a/src/game.odin b/src/game.odin index f841ccb..41dba42 100644 --- a/src/game.odin +++ b/src/game.odin @@ -31,8 +31,8 @@ Game :: struct { deck: [dynamic]Card, } -hand_size :: proc(s: Game) -> int { - return s.num_players <= 3 ? 5 : 4 +hand_size :: proc(g: Game) -> int { + return g.num_players <= 3 ? 5 : 4 } create_deck :: proc() -> (deck: [dynamic]Card) { @@ -45,9 +45,9 @@ create_deck :: proc() -> (deck: [dynamic]Card) { return } -create_player :: proc(s: ^Game) -> (p: Player) { - for i in 0 ..< hand_size(s^) { - append(&p.hand, pop(&s.deck)) +create_player :: proc(g: ^Game) -> (p: Player) { + for i in 0 ..< hand_size(g^) { + append(&p.hand, pop(&g.deck)) } return } @@ -63,8 +63,16 @@ create_game :: proc(hint_tokens, lives_left, num_players: int) -> (s: Game) { return } -print_game :: proc(s: Game) { - for p in s.players { +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) diff --git a/src/main.odin b/src/main.odin index 576ba25..ca5013e 100644 --- a/src/main.odin +++ b/src/main.odin @@ -37,7 +37,7 @@ main :: proc() { if !endpoint_ok do panic(fmt.tprintln("failed to parse endpoint:", os.args[2])) num_players, int_ok := strconv.parse_int(os.args[3]) if !int_ok do panic(fmt.tprintln("failed to parse number of players:", os.args[3])) - if num_players < 2 || num_players > 5 do return + if num_players < 1 || num_players > 5 do return run_server(addr, num_players) case: return diff --git a/src/server.odin b/src/server.odin index fcfd4e8..b95bc7c 100644 --- a/src/server.odin +++ b/src/server.odin @@ -1,23 +1,106 @@ package hanabi import "core:fmt" +import "core:math/rand" import "core:net" -run_server :: proc(listen_addr: net.Endpoint, num_players: int) { +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, +) { + 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) +} + + +/* +emulates Card, but packs the fields more neatly +*/ +Bit_Card :: bit_field u8 { + value: int | 3, + color: COLORS | 3, +} + +Payload :: struct #packed { + num_hints: u8, + num_lives: u8, + num_cards: u8, + cards: []Bit_Card, +} + +create_payload :: proc(g: Game, player: int, allocator := context.allocator) -> Payload { + cards: [dynamic]Bit_Card + for i in 0 ..< g.num_players { + if i == player do continue + hand := g.players[i].hand[:] + for c in hand { + append(&cards, Bit_Card{value = c.value, color = c.color}) + } + } + return {u8(g.hint_tokens), u8(g.lives_left), u8(len(cards)), cards[:]} +} + +send_payload :: proc(sock: net.TCP_Socket, pl: Payload) -> net.Network_Error { + data: [dynamic]byte + defer delete(data) + append(&data, 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 +} + +DEFAULT_HINT_TOKENS :: 8 +DEFAULT_LIVES :: 3 + +run_server :: proc( + listen_addr: net.Endpoint, + num_players: int, + num_hints := DEFAULT_HINT_TOKENS, + num_lives := DEFAULT_LIVES, +) { listener, list_err := net.listen_tcp(listen_addr) if list_err != nil do panic(fmt.tprintln("failed to listen:", net.to_string(listen_addr))) defer net.close(listener) - players: [dynamic]net.TCP_Socket - defer delete(players) + w, comm_err := create_comm_world(listener, num_players) + if comm_err != nil do panic(fmt.tprintln("failed to create comm world:", comm_err)) + defer delete_comm_world(&w) - for len(players) < num_players { - client_sock, source, err := net.accept_tcp(listener) - if err != nil { - fmt.eprintln("failed to connect:", err, source) + g := create_game(num_hints, num_lives, num_players) + defer delete_game(&g) + + current_player := rand.int_max(num_players) + game_loop: for { + for i in 0 ..< num_players { + pl := create_payload(g, i, context.temp_allocator) + send_err := send_payload(w.sockets[i], pl) + if send_err != nil do panic("failed to send payload") } - append(&players, client_sock) - fmt.println("player connected:", net.to_string(source)) + + fmt.printfln("player %s's turn!", current_player) + + // body + + current_player = (current_player + 1) % num_players + free_all(context.temp_allocator) } }