More experiments
This commit is contained in:
@@ -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,
|
||||
// ...
|
||||
})
|
||||
})
|
||||
// ...
|
||||
```
|
||||
|
||||
+23
-23
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
+52
-21
@@ -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
|
||||
|
||||
Request Cmd // TODO: Work in progress
|
||||
type Cmd interface {
|
||||
TargetPlayer() string
|
||||
}
|
||||
|
||||
// PlayerMessage è una risposta da parte del giocatore ad un comando
|
||||
type PlayerMessage struct {
|
||||
TargetPlayer string
|
||||
type baseCmd struct {
|
||||
targetPlayer string
|
||||
}
|
||||
|
||||
Response Msg // TODO: Work in progress
|
||||
func (c baseCmd) TargetPlayer() string {
|
||||
return c.targetPlayer
|
||||
}
|
||||
|
||||
// Messages
|
||||
|
||||
type Msg interface {
|
||||
TargetPlayer() string
|
||||
}
|
||||
|
||||
type baseMsg struct {
|
||||
targetPlayer string
|
||||
}
|
||||
|
||||
func (m baseMsg) TargetPlayer() string {
|
||||
return m.targetPlayer
|
||||
}
|
||||
|
||||
// -------------------- //
|
||||
// Commands and Message //
|
||||
// -------------------- //
|
||||
|
||||
//
|
||||
// Choose Option Form
|
||||
//
|
||||
|
||||
type ChooseOptionCmd struct {
|
||||
baseCmd
|
||||
Message string
|
||||
Options []string
|
||||
}
|
||||
|
||||
func NewChooseOptionCmd(player, message string, options []string) Cmd {
|
||||
return &ChooseOptionCmd{baseCmd{player}, message, options}
|
||||
}
|
||||
|
||||
type ChooseOptionMsg struct {
|
||||
baseMsg
|
||||
Answer string
|
||||
}
|
||||
|
||||
//
|
||||
// Cmd & Msg
|
||||
// Display Message Form
|
||||
//
|
||||
|
||||
type ChoosePlayerCmd struct {
|
||||
Players []string
|
||||
type DisplayMessageCmd struct {
|
||||
baseMsg
|
||||
Message string
|
||||
}
|
||||
|
||||
func (ChoosePlayerCmd) Cmd() {}
|
||||
|
||||
type ChoosePlayerMsg struct {
|
||||
Player string
|
||||
func NewDisplayCmd(option, message string) Cmd {
|
||||
return &DisplayMessageCmd{baseMsg{option}, message}
|
||||
}
|
||||
|
||||
func (ChoosePlayerMsg) Msg() {}
|
||||
|
||||
+54
-9
@@ -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
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
return state, []PlayerCommand{}
|
||||
// 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
|
||||
}
|
||||
|
||||
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, []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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user