wip
This commit is contained in:
+36
-56
@@ -2,70 +2,19 @@ package hanabi
|
|||||||
|
|
||||||
import "core:bufio"
|
import "core:bufio"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:io"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:net"
|
import "core:net"
|
||||||
import "core:os"
|
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) {
|
run_client :: proc(host_addr: net.Endpoint) {
|
||||||
serv, list_err := net.dial_tcp(host_addr)
|
serv, list_err := net.dial_tcp(host_addr)
|
||||||
if list_err != nil do panic(fmt.tprintln("failed to dial:", net.to_string(host_addr)))
|
if list_err != nil do panic(fmt.tprintln("failed to dial:", net.to_string(host_addr)))
|
||||||
defer net.close(serv)
|
defer net.close(serv)
|
||||||
|
|
||||||
|
// player input from stdin
|
||||||
scanner: bufio.Scanner
|
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 {
|
LISTEN: for {
|
||||||
msg_header: Msg_Type
|
msg_header: Msg_Type
|
||||||
@@ -77,11 +26,42 @@ run_client :: proc(host_addr: net.Endpoint) {
|
|||||||
action := bufio.scanner_text(&scanner)
|
action := bufio.scanner_text(&scanner)
|
||||||
net.send(serv, transmute([]byte)action)
|
net.send(serv, transmute([]byte)action)
|
||||||
case .State_Update:
|
case .State_Update:
|
||||||
pl := recv_payload(serv, context.temp_allocator)
|
view, recv_err := receive_view(serv)
|
||||||
view := create_view(pl, context.temp_allocator)
|
if recv_err != nil do panic(fmt.tprintln("failed to receive view", recv_err))
|
||||||
render_view(view)
|
render_view(view)
|
||||||
|
case .Info:
|
||||||
|
unimplemented()
|
||||||
}
|
}
|
||||||
free_all(context.temp_allocator)
|
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}
|
VALUES :: []u8{1, 1, 1, 2, 2, 3, 3, 4, 4, 5}
|
||||||
|
|
||||||
COLORS :: enum u8 {
|
COLORS :: enum u8 {
|
||||||
RED,
|
RED = 0b000001,
|
||||||
GREEN,
|
GREEN = 0b000010,
|
||||||
WHITE,
|
WHITE = 0b000100,
|
||||||
BLUE,
|
BLUE = 0b001000,
|
||||||
YELLOW,
|
YELLOW = 0b010000,
|
||||||
RAINBOW,
|
RAINBOW = 0b100000,
|
||||||
}
|
}
|
||||||
|
|
||||||
Card :: bit_field u8 {
|
Card :: bit_field u8 {
|
||||||
@@ -26,14 +26,24 @@ Hand :: distinct []Card
|
|||||||
// player can only place card of value `played[color]+1`.
|
// player can only place card of value `played[color]+1`.
|
||||||
Played_Card_Piles :: distinct [6]u8
|
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 {
|
Game :: struct {
|
||||||
num_players: int,
|
num_players: int,
|
||||||
num_hints: int,
|
num_hints: int,
|
||||||
num_lives: int,
|
num_lives: int,
|
||||||
hand_size: int,
|
hand_size: int,
|
||||||
player_hands: [dynamic]Hand,
|
played: Played_Card_Piles,
|
||||||
deck: [dynamic]Card,
|
player_hands: []Hand,
|
||||||
played: Played_Card_Piles,
|
player_beliefs: []Belief_State,
|
||||||
|
deck: [dynamic]Card,
|
||||||
}
|
}
|
||||||
|
|
||||||
create_deck :: proc() -> (deck: [dynamic]Card) {
|
create_deck :: proc() -> (deck: [dynamic]Card) {
|
||||||
@@ -48,18 +58,33 @@ create_deck :: proc() -> (deck: [dynamic]Card) {
|
|||||||
|
|
||||||
deal_hands :: proc(
|
deal_hands :: proc(
|
||||||
deck: ^[dynamic]Card,
|
deck: ^[dynamic]Card,
|
||||||
num_players: int,
|
num_players, hand_size: int,
|
||||||
hand_size: int,
|
allocator := context.allocator,
|
||||||
) -> (
|
) -> []Hand {
|
||||||
player_hands: [dynamic]Hand,
|
player_hands: [dynamic]Hand
|
||||||
) {
|
|
||||||
for i in 0 ..< num_players {
|
for i in 0 ..< num_players {
|
||||||
start := len(deck) - hand_size
|
start := len(deck) - hand_size
|
||||||
hand := Hand(deck[start:])
|
hand := Hand(deck[start:])
|
||||||
append(&player_hands, hand)
|
append(&player_hands, hand)
|
||||||
resize(deck, start)
|
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) {
|
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.hand_size = num_players <= 3 ? 5 : 4
|
||||||
s.deck = create_deck()
|
s.deck = create_deck()
|
||||||
s.player_hands = deal_hands(&s.deck, num_players, s.hand_size)
|
s.player_hands = deal_hands(&s.deck, num_players, s.hand_size)
|
||||||
|
s.player_beliefs = init_beliefs(num_players, s.hand_size)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-9
@@ -154,10 +154,20 @@ send_payload :: proc(sock: net.TCP_Socket, pl: Payload) -> net.Network_Error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
send_state_update :: proc(sock: net.TCP_Socket, pl: ^Player_View) -> net.Network_Error {
|
send_state_update :: proc(sock: net.TCP_Socket, view: ^Player_View) -> net.Network_Error {
|
||||||
header := Msg_Type.State_Update
|
// header := Msg_Type.State_Update
|
||||||
net.send(sock, mem.ptr_to_bytes(&header)) or_return
|
// net.send(sock, mem.ptr_to_bytes(&header)) or_return
|
||||||
net.send(sock, mem.ptr_to_bytes(pl)) 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +196,15 @@ send_info :: proc(sock: net.TCP_Socket, data: Info_Data) -> net.Network_Error {
|
|||||||
|
|
||||||
Play_Action :: distinct Card
|
Play_Action :: distinct Card
|
||||||
Discard_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 {
|
Action :: union {
|
||||||
Play_Action,
|
Play_Action,
|
||||||
@@ -195,10 +213,7 @@ Action :: union {
|
|||||||
}
|
}
|
||||||
|
|
||||||
receive_action :: proc(player: net.TCP_Socket) -> Action {
|
receive_action :: proc(player: net.TCP_Socket) -> Action {
|
||||||
unimplemented()
|
// validates user input and creates correct action
|
||||||
}
|
|
||||||
|
|
||||||
validate_action :: proc(action: Action) -> bool {
|
|
||||||
unimplemented()
|
unimplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user