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) } } // 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 ) // 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] }