More experiments

main
Antonio De Lucreziis 2 years ago
parent 5aad886817
commit bedde32d1d

@ -81,13 +81,13 @@ Lo stato della partita è contenuto in `lupus.PartitaState` e viene aggiornato d
Supponiamo ad esempio che sia la prima notte a questo punto, intanto aggiorniamo, gli unici ruoli che agiscono sono il _veggente_ e la _fattucchiera_ quindi come nuovo stato ritorniamo lo stesso stato solo con solo cambiato `state.Time = 1` e come lista di comandi ritorniamo i form per agire con le liste di tutti i giocatori (per ora sono tutti vivi quindi è la stessa lista). Supponiamo ad esempio che sia la prima notte a questo punto, intanto aggiorniamo, gli unici ruoli che agiscono sono il _veggente_ e la _fattucchiera_ quindi come nuovo stato ritorniamo lo stesso stato solo con solo cambiato `state.Time = 1` e come lista di comandi ritorniamo i form per agire con le liste di tutti i giocatori (per ora sono tutti vivi quindi è la stessa lista).
```go ```go
[]PlayerCmd{ []Cmd{
ChoosePlayerCmd{ ChooseOptionCmd{
Target: "player-i", // veggente Target: "player-i", // veggente
Message: "Chi vuoi puntare?", Message: "Chi vuoi puntare?",
Options: allPlayers, Options: allPlayers,
}, },
ChoosePlayerCmd{ ChooseOptionCmd{
Target: "player-j", // fattucchiera Target: "player-j", // fattucchiera
Message: "Chi vuoi puntare?", Message: "Chi vuoi puntare?",
Options: allPlayers, Options: allPlayers,
@ -98,12 +98,12 @@ Lo stato della partita è contenuto in `lupus.PartitaState` e viene aggiornato d
A questo punto il codice relativo alla partita va in pausa ed una volta che entrambi i giocatori hanno compilato i propri form la risposta sarà (ad esempio diciamo che sia la _fattucchiera_ che il _veggente_ hanno puntato `player-1` che è un _contadino_) A questo punto il codice relativo alla partita va in pausa ed una volta che entrambi i giocatori hanno compilato i propri form la risposta sarà (ad esempio diciamo che sia la _fattucchiera_ che il _veggente_ hanno puntato `player-1` che è un _contadino_)
```go ```go
[]PlayerMsg{ []Msg{
ChoosePlayerMsg{ ChooseOptionMsg{
Target: "player-i", // veggente Target: "player-i", // veggente
Answer: "player-1", Answer: "player-1",
}, },
ChoosePlayerMsg{ ChooseOptionMsg{
Target: "player-j", // fattucchiera Target: "player-j", // fattucchiera
Answer: "player-1", Answer: "player-1",
}, },
@ -119,36 +119,37 @@ Lo stato della partita è contenuto in `lupus.PartitaState` e viene aggiornato d
}, },
``` ```
ora però è giorno ed i giocatori dovranno anche votare per il rogo quindi dobbiamo anche inviare una lista di messaggio di scelta per il voto come segue ora però è giorno ed i giocatori dovranno anche votare per il rogo quindi dobbiamo anche inviare una lista di messaggio di scelta per il voto come segue. Inoltre se è stato incluso il kamikaze bisogna anche aggiungere un messaggio per far agire il kamikaze durante il giorno
```go ```go
ChoosePlayerCmd{ OrthogonalCmd{
Cases: [][]Cmd{
[]Cmd{
ChooseOptionCmd{
Target: "player-1", Target: "player-1",
Message: "Chi vuoi bruciare al rogo?", Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers, Options: allAlivePlayers,
}, },
ChoosePlayerCmd{ ChooseOptionCmd{
Target: "player-2", Target: "player-2",
Message: "Chi vuoi bruciare al rogo?", Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers, Options: allAlivePlayers,
}, },
... ...
ChoosePlayerCmd{ ChooseOptionCmd{
Target: "player-n", Target: "player-n",
Message: "Chi vuoi bruciare al rogo?", Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers, Options: allAlivePlayers,
}, },
``` },
[]Cmd{
inoltre se è stato incluso il kamikaze bisogna anche aggiungere un messaggio per far agire il kamikaze durante il giorno ChooseOptionCmd{
```go
OptionalCmd{
ChoosePlayerCmd{
Target: "player-?", // kamikaze Target: "player-?", // kamikaze
Message: "Su chi vuoi farti esplodere?", Message: "Su chi vuoi farti esplodere?",
Options: allAlivePlayers, Options: allAlivePlayers,
} }
},
},
} }
``` ```
@ -157,3 +158,33 @@ Lo stato della partita è contenuto in `lupus.PartitaState` e viene aggiornato d
## Architettura Lupus (2) ## Architettura Lupus (2)
TODO: Boh forse si può fare proprio qualcosa ad "eventi". TODO: Boh forse si può fare proprio qualcosa ad "eventi".
```go
// ...
partita.On("player_action", func (e Event) {
sourcePlayer := e.Custom("source_player")
targetPlayer := e.Custom("target_player")
partita.FaseActionsCount++
partita.Actions = append(partita.Actions, &Action{
Time: partita.Time,
// ...
})
if partita.FaseActionsCount == partita.FaseActingPlayerCount {
partita.Dispatch("end_fase")
}
})
partita.On("kamikaze_explode", func (e Event) {
sourcePlayer := e.Custom("source_player")
targetPlayer := e.Custom("target_player")
partita.Actions = append(partita.Actions, &Action{
Time: partita.Time,
// ...
})
})
// ...
```

@ -43,43 +43,43 @@ type Ruolo struct {
Aura string `json:"aura"` Aura string `json:"aura"`
} }
func RuoloMainCmd(player Player, state PartitaState) (cmd PlayerCommand, noop bool) { var defaultMessageMap = map[Ruolo]string{
Veggente: "Chi vuoi veggiare?",
Lupo: "Chi vuoi uccidere?",
Cacciatore: "Chi vuoi cacciare?",
Guardia: "Chi vuoi guardiare?",
Medium: "Chi vuoi mediare?",
Fattucchiera: "Chi vuoi fattuccare?",
Kamikaze: "Su chi vuoi farti esplodere?",
}
func RuoloMainCmd(player Player, state PartitaState) Cmd {
options := []string{}
switch player.Ruolo { switch player.Ruolo {
case Veggente, Lupo, Cacciatore, Guardia, Kamikaze: case Veggente, Lupo, Cacciatore, Guardia, Kamikaze:
alivePlayers := []string{}
for _, p := range state.Players { for _, p := range state.Players {
if p.Vivo { if p.Vivo {
alivePlayers = append(alivePlayers, p.Username) options = append(options, p.Username)
} }
} }
return PlayerCommand{
TargetPlayer: player.Username,
Request: ChoosePlayerCmd{alivePlayers},
}, false
case Medium: case Medium:
deadPlayers := []string{}
for _, p := range state.Players { for _, p := range state.Players {
if !p.Vivo { if !p.Vivo {
deadPlayers = append(deadPlayers, p.Username) options = append(options, p.Username)
} }
} }
return PlayerCommand{
TargetPlayer: player.Username,
Request: ChoosePlayerCmd{deadPlayers},
}, false
case Fattucchiera: case Fattucchiera:
allPlayers := []string{}
for _, p := range state.Players { for _, p := range state.Players {
allPlayers = append(allPlayers, p.Username) options = append(options, p.Username)
} }
return PlayerCommand{
TargetPlayer: player.Username,
Request: ChoosePlayerCmd{allPlayers},
}, false
default: default:
return PlayerCommand{}, true return nil
} }
return NewChooseOptionCmd(
player.Username,
defaultMessageMap[player.Ruolo],
options,
)
} }

@ -6,38 +6,69 @@ type Ruleset struct {
Start func(PartitaConfig) PartitaState Start func(PartitaConfig) PartitaState
// Update prende lo stato della partita e ne ritorna uno nuovo eventualmente utilizzando delle "UserResponse" fatte precedentemente all'utente (alla prima chiamata questa lista è vuota), successivamente c'è una corrispondenza tra lo slice di "[]UserRequest" ritornato ed il successivo slice "[]UserResponse" ricevuto. // Update prende lo stato della partita e ne ritorna uno nuovo eventualmente utilizzando delle "UserResponse" fatte precedentemente all'utente (alla prima chiamata questa lista è vuota), successivamente c'è una corrispondenza tra lo slice di "[]UserRequest" ritornato ed il successivo slice "[]UserResponse" ricevuto.
Update func(state PartitaState, responses []PlayerMessage) (PartitaState, []PlayerCommand) Update func(state PartitaState, responses []Msg) (PartitaState, []Cmd)
} }
type Cmd interface{ Cmd() } // Commands
type Msg interface{ Msg() }
// PlayerCommand rappresenta un "comando" per un certo giocatore, nella fattispecie può dire di mostrare al giocatore un messaggio o anche un prompt che chiede qualcosa all'utente type Cmd interface {
type PlayerCommand struct { TargetPlayer() string
TargetPlayer string }
type baseCmd struct {
targetPlayer string
}
func (c baseCmd) TargetPlayer() string {
return c.targetPlayer
}
// Messages
Request Cmd // TODO: Work in progress type Msg interface {
TargetPlayer() string
} }
// PlayerMessage è una risposta da parte del giocatore ad un comando type baseMsg struct {
type PlayerMessage struct { targetPlayer string
TargetPlayer string }
Response Msg // TODO: Work in progress func (m baseMsg) TargetPlayer() string {
return m.targetPlayer
} }
// -------------------- //
// Commands and Message //
// -------------------- //
// //
// Cmd & Msg // Choose Option Form
// //
type ChoosePlayerCmd struct { type ChooseOptionCmd struct {
Players []string baseCmd
Message string
Options []string
} }
func (ChoosePlayerCmd) Cmd() {} func NewChooseOptionCmd(player, message string, options []string) Cmd {
return &ChooseOptionCmd{baseCmd{player}, message, options}
}
type ChoosePlayerMsg struct { type ChooseOptionMsg struct {
Player string baseMsg
Answer string
} }
func (ChoosePlayerMsg) Msg() {} //
// Display Message Form
//
type DisplayMessageCmd struct {
baseMsg
Message string
}
func NewDisplayCmd(option, message string) Cmd {
return &DisplayMessageCmd{baseMsg{option}, message}
}

@ -1,11 +1,15 @@
package lupus package lupus
import ( import (
"fmt"
"log" "log"
"math/rand"
"github.com/aziis98/lupus-lite/util" "github.com/aziis98/lupus-lite/util"
) )
const MessageRogo = "Chi vuoi bruciare al rogo?"
var Ruleset1 = Ruleset{ var Ruleset1 = Ruleset{
Start: func(config PartitaConfig) PartitaState { Start: func(config PartitaConfig) PartitaState {
state := PartitaState{ state := PartitaState{
@ -40,29 +44,70 @@ var Ruleset1 = Ruleset{
return state return state
}, },
Update: func(state PartitaState, responses []PlayerMessage) (PartitaState, []PlayerCommand) { Update: func(state PartitaState, responses []Msg) (PartitaState, []Cmd) {
playerCommands := []Cmd{}
if state.Time == 0 { if state.Time == 0 {
ruoliAgenti := map[Ruolo]struct{}{ ruoliAgenti := map[Ruolo]struct{}{
Fattucchiera: {}, Fattucchiera: {},
Veggente: {}, Veggente: {},
} }
playerCommands := []PlayerCommand{}
for _, player := range state.Players { for _, player := range state.Players {
if _, present := ruoliAgenti[player.Ruolo]; present { if _, present := ruoliAgenti[player.Ruolo]; present {
if cmd, noop := RuoloMainCmd(player, state); !noop { playerCommands = append(playerCommands, RuoloMainCmd(player, state))
playerCommands = append(playerCommands, cmd) }
}
state.Time++
return state, playerCommands
}
// di giorno i giocatori vivi ricevono un form per votare qualcuno da bruciare al rogo e possono scegliere solo tra i vivi
if state.IsGiorno() {
alivePlayers := []string{}
for _, player := range state.Players {
if player.Vivo {
alivePlayers = append(alivePlayers, player.Username)
} }
} }
for _, option := range alivePlayers {
playerCommands = append(playerCommands, NewChooseOptionCmd(
option,
MessageRogo,
alivePlayers,
))
} }
state.Time++ state.Time++
return state, playerCommands return state, playerCommands
} else { }
if state.IsNotte() {
i := rand.Intn(len(state.Players))
diedPlayer := state.Players[i]
state.Players[i].Vivo = false
alivePlayers := []string{}
for _, player := range state.Players {
if player.Vivo {
alivePlayers = append(alivePlayers, player.Username)
}
}
for _, playerId := range alivePlayers {
playerCommands = append(playerCommands, NewDisplayCmd(
playerId,
fmt.Sprintf("@%s è morto questa notte", diedPlayer.Username),
))
}
state.Time++
return state, playerCommands
} }
return state, []PlayerCommand{} return state, []Cmd{}
}, },
} }

@ -56,3 +56,30 @@ func Shuffle[T any](slice []T) {
slice[i], slice[j] = slice[j], slice[i] slice[i], slice[j] = slice[j], slice[i]
}) })
} }
func Sample[T any](vs []T, count int) []T {
buffer := append([]T{}, vs...)
result := make([]T, count)
for i := 0; i < count; i++ {
n := rand.Intn(len(buffer))
result[i], buffer = buffer[n], append(buffer[:n], buffer[n+1:]...)
}
return result
}
func GroupBy[K comparable, V any](vs []V, keyFn func(V) K) map[K][]V {
r := map[K][]V{}
for _, v := range vs {
key := keyFn(v)
group, ok := r[key]
if !ok {
group = []V{}
}
r[key] = append(group, v)
}
return r
}

Loading…
Cancel
Save