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).
```go
[]PlayerCmd{
ChoosePlayerCmd{
[]Cmd{
ChooseOptionCmd{
Target: "player-i", // veggente
Message: "Chi vuoi puntare?",
Options: allPlayers,
},
ChoosePlayerCmd{
ChooseOptionCmd{
Target: "player-j", // fattucchiera
Message: "Chi vuoi puntare?",
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_)
```go
[]PlayerMsg{
ChoosePlayerMsg{
[]Msg{
ChooseOptionMsg{
Target: "player-i", // veggente
Answer: "player-1",
},
ChoosePlayerMsg{
ChooseOptionMsg{
Target: "player-j", // fattucchiera
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
ChoosePlayerCmd{
Target: "player-1",
Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers,
},
ChoosePlayerCmd{
Target: "player-2",
Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers,
},
...
ChoosePlayerCmd{
Target: "player-n",
Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers,
},
```
inoltre se è stato incluso il kamikaze bisogna anche aggiungere un messaggio per far agire il kamikaze durante il giorno
```go
OptionalCmd{
ChoosePlayerCmd{
Target: "player-?", // kamikaze
Message: "Su chi vuoi farti esplodere?",
Options: allAlivePlayers,
}
OrthogonalCmd{
Cases: [][]Cmd{
[]Cmd{
ChooseOptionCmd{
Target: "player-1",
Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers,
},
ChooseOptionCmd{
Target: "player-2",
Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers,
},
...
ChooseOptionCmd{
Target: "player-n",
Message: "Chi vuoi bruciare al rogo?",
Options: allAlivePlayers,
},
},
[]Cmd{
ChooseOptionCmd{
Target: "player-?", // kamikaze
Message: "Su chi vuoi farti esplodere?",
Options: allAlivePlayers,
}
},
},
}
```
@ -157,3 +158,33 @@ Lo stato della partita è contenuto in `lupus.PartitaState` e viene aggiornato d
## Architettura Lupus (2)
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"`
}
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 {
case Veggente, Lupo, Cacciatore, Guardia, Kamikaze:
alivePlayers := []string{}
for _, p := range state.Players {
if p.Vivo {
alivePlayers = append(alivePlayers, p.Username)
options = append(options, p.Username)
}
}
return PlayerCommand{
TargetPlayer: player.Username,
Request: ChoosePlayerCmd{alivePlayers},
}, false
case Medium:
deadPlayers := []string{}
for _, p := range state.Players {
if !p.Vivo {
deadPlayers = append(deadPlayers, p.Username)
options = append(options, p.Username)
}
}
return PlayerCommand{
TargetPlayer: player.Username,
Request: ChoosePlayerCmd{deadPlayers},
}, false
case Fattucchiera:
allPlayers := []string{}
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:
return PlayerCommand{}, true
return nil
}
return NewChooseOptionCmd(
player.Username,
defaultMessageMap[player.Ruolo],
options,
)
}

@ -6,38 +6,69 @@ type Ruleset struct {
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 func(state PartitaState, responses []PlayerMessage) (PartitaState, []PlayerCommand)
Update func(state PartitaState, responses []Msg) (PartitaState, []Cmd)
}
type Cmd interface{ Cmd() }
type Msg interface{ Msg() }
// Commands
// 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 PlayerCommand struct {
TargetPlayer string
type Cmd interface {
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 PlayerMessage struct {
TargetPlayer string
type baseMsg struct {
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 {
Players []string
type ChooseOptionCmd struct {
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 {
Player string
type ChooseOptionMsg struct {
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
import (
"fmt"
"log"
"math/rand"
"github.com/aziis98/lupus-lite/util"
)
const MessageRogo = "Chi vuoi bruciare al rogo?"
var Ruleset1 = Ruleset{
Start: func(config PartitaConfig) PartitaState {
state := PartitaState{
@ -40,29 +44,70 @@ var Ruleset1 = Ruleset{
return state
},
Update: func(state PartitaState, responses []PlayerMessage) (PartitaState, []PlayerCommand) {
Update: func(state PartitaState, responses []Msg) (PartitaState, []Cmd) {
playerCommands := []Cmd{}
if state.Time == 0 {
ruoliAgenti := map[Ruolo]struct{}{
Fattucchiera: {},
Veggente: {},
}
playerCommands := []PlayerCommand{}
for _, player := range state.Players {
if _, present := ruoliAgenti[player.Ruolo]; present {
if cmd, noop := RuoloMainCmd(player, state); !noop {
playerCommands = append(playerCommands, cmd)
}
playerCommands = append(playerCommands, RuoloMainCmd(player, state))
}
}
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++
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]
})
}
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