191 lines
6.2 KiB
Go
191 lines
6.2 KiB
Go
package rooms
|
|
|
|
import (
|
|
"log"
|
|
"maps"
|
|
"slices"
|
|
)
|
|
|
|
type Room struct {
|
|
Name string // will be unique ID
|
|
AdminIds []PersonId
|
|
PasswordHash string
|
|
CurrentSpeaker PersonId // i guess let's set to zero value when it's noone from the room
|
|
// all people that were visiting room before, spectating now or being a participant
|
|
// used to check person password against the name
|
|
AllKnownPeople []Person
|
|
// person ids of people who are currently participating in the discussion at the table
|
|
// TODO for now peson in seated at join and removed at logout in routes
|
|
Paricipants []PersonId
|
|
ParticipantHands map[PersonId]HandGesture
|
|
Marks map[HandGesture]PersonId
|
|
}
|
|
|
|
func (r *Room)InitMaps() {
|
|
if r.ParticipantHands == nil {
|
|
r.ParticipantHands = make(map[PersonId]HandGesture)
|
|
}
|
|
if r.Marks == nil {
|
|
r.Marks = make(map[HandGesture]PersonId)
|
|
}
|
|
}
|
|
|
|
// if you have no hand raised - select any hand
|
|
// if you are not speaking and have a hand raise - just exchange
|
|
// if you are speaking - change nothing
|
|
// if nobody is speaking, set this person as a first speaker
|
|
func (r *Room) RaiseHand(p PersonId, gesture HandGesture) Room {
|
|
if (r.CurrentSpeaker == p) {
|
|
// if person already speaking, should first end speaking
|
|
return *r
|
|
}
|
|
r.ParticipantHands[p] = gesture
|
|
if r.CurrentSpeaker == PersonId(0) {
|
|
r.CurrentSpeaker = p
|
|
}
|
|
return *r
|
|
}
|
|
|
|
// and how would i organize?
|
|
// i should have map[HandGesture]ParticipantId as mark. for 'from where to go clockwise if returning a level lover'
|
|
// now i want methods that for some person raise some hand, so i guess it adds what? to map[ParticipantId]HandGesture
|
|
// i suppose methods should be on a room, in a session i'd have ParticipantId to pass in, and would load room
|
|
// and i want all parts of room in same struct, because i'd want to subscribe to changes to all of them
|
|
|
|
// i suppose raising a hand could be just in place adding PersonId->HandGesture,
|
|
// but releasing hand should have additional logic
|
|
func (r *Room) ReleaseHand(p PersonId) {
|
|
// releasing a hand of a current speaker should result in selection of a new speaker
|
|
log.Printf("about to release hand of %d in %s", p, r.Name)
|
|
|
|
// first remove hand of the requested person
|
|
currentSpeakerGesture, currentSpeakerHandFound := r.ParticipantHands[p]
|
|
if !currentSpeakerHandFound {
|
|
return
|
|
}
|
|
|
|
delete(r.ParticipantHands, p)
|
|
|
|
// if not a current speaker, no complicated logic required to release a hand
|
|
if r.CurrentSpeaker != p {
|
|
return
|
|
}
|
|
|
|
// if a current speaker - after removing the hand, we need to find next speaker
|
|
// from highest hand gesture to lowest, until one is found
|
|
gestures := [...]HandGesture{Meta, ClarifyingQ, Expand, ProbingQ, ChangeTopic}
|
|
|
|
var nextSpeakerId PersonId
|
|
var nextSpeakerFound bool
|
|
|
|
gestureIteration:
|
|
for _, gesture := range gestures {
|
|
log.Printf("searching for gesture %s", gesture.String())
|
|
startIndex := r.gestureSearchStartIndex(gesture, currentSpeakerGesture)
|
|
participantsCount := len(r.Paricipants)
|
|
for i := 1; i < participantsCount; i++ {
|
|
checkIndex := (startIndex + i) % participantsCount
|
|
checkPerson := r.Paricipants[checkIndex]
|
|
checkGesture, isFound := r.ParticipantHands[checkPerson]
|
|
if isFound && checkGesture == gesture {
|
|
nextSpeakerId, nextSpeakerFound = checkPerson, true
|
|
break gestureIteration
|
|
}
|
|
}
|
|
}
|
|
if !nextSpeakerFound {
|
|
log.Printf("there is not next speaker, that's ok")
|
|
}
|
|
|
|
nextSpeakerGesture := r.ParticipantHands[nextSpeakerId]
|
|
log.Printf("found next speaker %+v", nextSpeakerId)
|
|
|
|
if nextSpeakerGesture > currentSpeakerGesture {
|
|
// raising the level of the speaker, need to save mark
|
|
log.Printf("we do have nextSpeaker of priority %s higher than current %s", nextSpeakerGesture.String(), currentSpeakerGesture.String())
|
|
r.Marks[currentSpeakerGesture] = p
|
|
}
|
|
|
|
// also we need to remove marks from top to current speaker level
|
|
for _, gesture := range gestures {
|
|
if gesture < nextSpeakerGesture {
|
|
break
|
|
}
|
|
delete(r.Marks, gesture)
|
|
}
|
|
|
|
r.CurrentSpeaker = nextSpeakerId
|
|
}
|
|
|
|
// find place to start - separate function
|
|
// current speaker level, searched gesture level, room marks
|
|
// if search level >= current speaker start from speaker
|
|
// if search level < current speaekr look for mark, start from mark of that level,
|
|
//
|
|
// if no mark - from speaker
|
|
func (r *Room) gestureSearchStartIndex(gesture, curSpeakerGesture HandGesture) int {
|
|
if r.CurrentSpeaker == PersonId(0) {
|
|
// nobody is actully a speaker
|
|
return 0
|
|
}
|
|
|
|
var personFromWhichToStartSearch PersonId = r.CurrentSpeaker
|
|
|
|
// if searched guesture is higher or same as current speaker, start from speaker
|
|
if gesture >= curSpeakerGesture {
|
|
personFromWhichToStartSearch = r.CurrentSpeaker
|
|
}
|
|
// if searched gesture is of lower priority from current speaker, check marks to return to, or use speaker level
|
|
if gesture < curSpeakerGesture {
|
|
gestureMark, found := r.Marks[gesture]
|
|
if found {
|
|
personFromWhichToStartSearch = gestureMark
|
|
}
|
|
// if no mark found - count from the speaker
|
|
}
|
|
log.Printf("> selecting person from which to start. cur speaker %s, for gestrue %s, got person %d",
|
|
curSpeakerGesture.String(), gesture.String(), personFromWhichToStartSearch)
|
|
|
|
indexFromWhichToStart := slices.Index(r.Paricipants, personFromWhichToStartSearch)
|
|
return indexFromWhichToStart
|
|
}
|
|
|
|
func (r *Room) Equal(other *Room) bool {
|
|
if r == other {
|
|
return true
|
|
}
|
|
if r.Name != other.Name || r.PasswordHash != other.PasswordHash || r.CurrentSpeaker != other.CurrentSpeaker {
|
|
return false
|
|
}
|
|
if !slices.Equal(r.AdminIds, other.AdminIds) || !slices.Equal(r.Paricipants, other.Paricipants) || !slices.Equal(r.AllKnownPeople, other.AllKnownPeople) {
|
|
return false
|
|
}
|
|
if !maps.Equal(r.ParticipantHands, other.ParticipantHands) || !maps.Equal(r.Marks, other.Marks) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// sooooooo. i need hand types. are there enums in go?
|
|
type HandGesture uint8
|
|
|
|
const (
|
|
ChangeTopic HandGesture = iota
|
|
ProbingQ
|
|
Expand
|
|
ClarifyingQ
|
|
Meta
|
|
)
|
|
|
|
func GestureFromInt(num int) (HandGesture, bool) {
|
|
if (num >= int(ChangeTopic) && num <= int(Meta)) {
|
|
return HandGesture(num), true
|
|
}
|
|
return HandGesture(0), false
|
|
}
|
|
|
|
// String() method to print the name of the days
|
|
func (g HandGesture) String() string {
|
|
return [...]string{"Change Topic", "Probing Quesion", "Expand", "Clarifying Quesion", "Meta"}[g]
|
|
}
|