forked from frero/hanabi
wip
This commit is contained in:
+36
-56
@@ -2,70 +2,19 @@ package hanabi
|
||||
|
||||
import "core:bufio"
|
||||
import "core:fmt"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:net"
|
||||
import "core:os"
|
||||
|
||||
Client_View :: struct {
|
||||
num_players: int,
|
||||
num_hints: int,
|
||||
num_lives: int,
|
||||
other_players: []Player,
|
||||
}
|
||||
|
||||
recv_payload :: proc(sock: net.TCP_Socket, allocator := context.allocator) -> (pl: Player_View) {
|
||||
header: [4]u8
|
||||
net.recv(sock, header[:])
|
||||
num_cards := header[3]
|
||||
body := make([]u8, num_cards)
|
||||
net.recv(sock, body)
|
||||
pl = {header[0], header[1], header[2], header[3], transmute([]Card)body}
|
||||
// sanity checks
|
||||
assert(2 <= pl.num_players && pl.num_players <= 5)
|
||||
assert(pl.num_hints >= 0 && pl.num_lives >= 0)
|
||||
for c in pl.cards do assert(1 <= c.value && c.value <= 5)
|
||||
return
|
||||
}
|
||||
|
||||
create_view :: proc(pl: Player_View, allocator := context.allocator) -> Client_View {
|
||||
cards_per_hand := pl.num_cards / (pl.num_players - 1)
|
||||
players: [dynamic]Player
|
||||
i := 0
|
||||
for cast(u8)len(players) < pl.num_players - 1 {
|
||||
hand: [dynamic]Card
|
||||
for cast(u8)len(hand) < cards_per_hand {
|
||||
append(&hand, pl.cards[i])
|
||||
i += 1
|
||||
}
|
||||
append(&players, Player{hand = hand})
|
||||
}
|
||||
|
||||
return {
|
||||
num_players = int(pl.num_players),
|
||||
num_hints = int(pl.num_hints),
|
||||
num_lives = int(pl.num_lives),
|
||||
other_players = players[:],
|
||||
}
|
||||
}
|
||||
|
||||
render_view :: proc(view: Client_View) {
|
||||
fmt.println("number of hints left:", view.num_hints)
|
||||
fmt.println("number of lives left:", view.num_lives)
|
||||
for p in view.other_players {
|
||||
fmt.println()
|
||||
for c in p.hand {
|
||||
fmt.println(c.value, c.color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run_client :: proc(host_addr: net.Endpoint) {
|
||||
serv, list_err := net.dial_tcp(host_addr)
|
||||
if list_err != nil do panic(fmt.tprintln("failed to dial:", net.to_string(host_addr)))
|
||||
defer net.close(serv)
|
||||
|
||||
// player input from stdin
|
||||
scanner: bufio.Scanner
|
||||
bufio.scanner_init(&scanner, os.stream_from_handle(os.stdin), context.temp_allocator)
|
||||
bufio.scanner_init(&scanner, io.to_reader(transmute(io.Stream)os.stdin.stream))
|
||||
|
||||
LISTEN: for {
|
||||
msg_header: Msg_Type
|
||||
@@ -77,11 +26,42 @@ run_client :: proc(host_addr: net.Endpoint) {
|
||||
action := bufio.scanner_text(&scanner)
|
||||
net.send(serv, transmute([]byte)action)
|
||||
case .State_Update:
|
||||
pl := recv_payload(serv, context.temp_allocator)
|
||||
view := create_view(pl, context.temp_allocator)
|
||||
view, recv_err := receive_view(serv)
|
||||
if recv_err != nil do panic(fmt.tprintln("failed to receive view", recv_err))
|
||||
render_view(view)
|
||||
case .Info:
|
||||
unimplemented()
|
||||
}
|
||||
free_all(context.temp_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
receive_view :: proc(
|
||||
serv: net.TCP_Socket,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
view: Player_View,
|
||||
err: net.Network_Error,
|
||||
) {
|
||||
// constant header
|
||||
header_size := offset_of(Player_View, cards)
|
||||
net.recv(serv, (cast([^]byte)&view)[:header_size]) or_return
|
||||
// body of cards
|
||||
body_data := make([]Card, view.num_cards)
|
||||
body := raw_data(view.cards[:])[:view.num_cards * size_of(Card)]
|
||||
net.recv(serv, transmute([]byte)body) or_return
|
||||
return
|
||||
}
|
||||
|
||||
render_view :: proc(view: Player_View) {
|
||||
fmt.println("GAME:")
|
||||
fmt.println("\tnumber of hints left:", view.game_state.num_hints)
|
||||
fmt.println("\tnumber of lives left:", view.game_state.num_lives)
|
||||
fmt.println("PLAYERS:")
|
||||
for p in 0 ..< view.constant.num_players - 1 {
|
||||
fmt.printfln("\tplayer %d's hand:", p)
|
||||
for c in view.cards {
|
||||
if c._player == p do fmt.println("\t\t", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-19
@@ -5,12 +5,12 @@ import "core:math/rand"
|
||||
VALUES :: []u8{1, 1, 1, 2, 2, 3, 3, 4, 4, 5}
|
||||
|
||||
COLORS :: enum u8 {
|
||||
RED,
|
||||
GREEN,
|
||||
WHITE,
|
||||
BLUE,
|
||||
YELLOW,
|
||||
RAINBOW,
|
||||
RED = 0b000001,
|
||||
GREEN = 0b000010,
|
||||
WHITE = 0b000100,
|
||||
BLUE = 0b001000,
|
||||
YELLOW = 0b010000,
|
||||
RAINBOW = 0b100000,
|
||||
}
|
||||
|
||||
Card :: bit_field u8 {
|
||||
@@ -26,14 +26,24 @@ Hand :: distinct []Card
|
||||
// player can only place card of value `played[color]+1`.
|
||||
Played_Card_Piles :: distinct [6]u8
|
||||
|
||||
Belief_State :: []bit_field u16 {
|
||||
// `11111` can be any value 1-5.
|
||||
// `00010` must be value 4.
|
||||
value: u8 | 5,
|
||||
// `111111` can be any color.
|
||||
// `100001` can be red or rainbow.
|
||||
color: u8 | 6,
|
||||
}
|
||||
|
||||
Game :: struct {
|
||||
num_players: int,
|
||||
num_hints: int,
|
||||
num_lives: int,
|
||||
hand_size: int,
|
||||
player_hands: [dynamic]Hand,
|
||||
deck: [dynamic]Card,
|
||||
played: Played_Card_Piles,
|
||||
num_players: int,
|
||||
num_hints: int,
|
||||
num_lives: int,
|
||||
hand_size: int,
|
||||
played: Played_Card_Piles,
|
||||
player_hands: []Hand,
|
||||
player_beliefs: []Belief_State,
|
||||
deck: [dynamic]Card,
|
||||
}
|
||||
|
||||
create_deck :: proc() -> (deck: [dynamic]Card) {
|
||||
@@ -48,18 +58,33 @@ create_deck :: proc() -> (deck: [dynamic]Card) {
|
||||
|
||||
deal_hands :: proc(
|
||||
deck: ^[dynamic]Card,
|
||||
num_players: int,
|
||||
hand_size: int,
|
||||
) -> (
|
||||
player_hands: [dynamic]Hand,
|
||||
) {
|
||||
num_players, hand_size: int,
|
||||
allocator := context.allocator,
|
||||
) -> []Hand {
|
||||
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
|
||||
return player_hands[:]
|
||||
}
|
||||
|
||||
init_beliefs :: proc(
|
||||
num_players, hand_size: int,
|
||||
allocator := context.allocator,
|
||||
) -> []Belief_State {
|
||||
player_beliefs: [dynamic]Belief_State
|
||||
for p in 0 ..< num_players {
|
||||
bs: Belief_State
|
||||
for c in 0 ..< hand_size {
|
||||
bs[c].value = 0b11111
|
||||
bs[c].color = 0b111111
|
||||
}
|
||||
append(&player_beliefs, bs)
|
||||
}
|
||||
return player_beliefs[:]
|
||||
}
|
||||
|
||||
create_game :: proc(hint_tokens, lives_left, num_players: int) -> (s: Game) {
|
||||
@@ -70,6 +95,7 @@ create_game :: proc(hint_tokens, lives_left, num_players: int) -> (s: Game) {
|
||||
s.hand_size = num_players <= 3 ? 5 : 4
|
||||
s.deck = create_deck()
|
||||
s.player_hands = deal_hands(&s.deck, num_players, s.hand_size)
|
||||
s.player_beliefs = init_beliefs(num_players, s.hand_size)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+24
-9
@@ -154,10 +154,20 @@ send_payload :: proc(sock: net.TCP_Socket, pl: Payload) -> net.Network_Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
send_state_update :: proc(sock: net.TCP_Socket, pl: ^Player_View) -> net.Network_Error {
|
||||
header := Msg_Type.State_Update
|
||||
net.send(sock, mem.ptr_to_bytes(&header)) or_return
|
||||
net.send(sock, mem.ptr_to_bytes(pl)) or_return
|
||||
send_state_update :: proc(sock: net.TCP_Socket, view: ^Player_View) -> net.Network_Error {
|
||||
// header := Msg_Type.State_Update
|
||||
// net.send(sock, mem.ptr_to_bytes(&header)) or_return
|
||||
// net.send(sock, mem.ptr_to_bytes(pl)) or_return
|
||||
// return nil
|
||||
type := Msg_Type.State_Update
|
||||
net.send(sock, mem.ptr_to_bytes(&type)) or_return
|
||||
// everything up to `cards` field ("header")
|
||||
header_size := offset_of(Player_View, cards)
|
||||
header_ptr := raw_data(mem.byte_slice(view, size_of(Player_View)))
|
||||
net.send(sock, header_ptr[:header_size]) or_return
|
||||
// send dynamic `cards` body field
|
||||
body := raw_data(view.cards)[:len(view.cards) * size_of(Card)]
|
||||
net.send(sock, transmute([]byte)body) or_return
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -186,7 +196,15 @@ send_info :: proc(sock: net.TCP_Socket, data: Info_Data) -> net.Network_Error {
|
||||
|
||||
Play_Action :: distinct Card
|
||||
Discard_Action :: distinct Card
|
||||
Hint_Action :: distinct Card
|
||||
Value_Hint :: distinct int
|
||||
Color_Hint :: distinct int
|
||||
Hint_Action :: struct {
|
||||
target_player: int, // global index
|
||||
type: union {
|
||||
Value_Hint,
|
||||
Color_Hint,
|
||||
},
|
||||
}
|
||||
|
||||
Action :: union {
|
||||
Play_Action,
|
||||
@@ -195,10 +213,7 @@ Action :: union {
|
||||
}
|
||||
|
||||
receive_action :: proc(player: net.TCP_Socket) -> Action {
|
||||
unimplemented()
|
||||
}
|
||||
|
||||
validate_action :: proc(action: Action) -> bool {
|
||||
// validates user input and creates correct action
|
||||
unimplemented()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user