Skip to content

Instantly share code, notes, and snippets.

@squiidz
Last active March 3, 2021 12:57
Show Gist options
  • Save squiidz/0aa472a0d4604a811e0067fd6e918af0 to your computer and use it in GitHub Desktop.
Save squiidz/0aa472a0d4604a811e0067fd6e918af0 to your computer and use it in GitHub Desktop.
Seats assignment

CMD: go run main.go R1C4 R1C6 R2C3 R2C7 R3C9 R3C10 then enter the number of seats you want to reserve

package main
import (
"bufio"
"errors"
"fmt"
"math"
"os"
"regexp"
"strconv"
)
var (
// Qualities is only used to print seats with different color based on their quality
Qualities = map[int]string{
-1: "\u001b[30m%s\033[92m",
0: "\u001b[32m%s\033[92m",
1: "\u001b[33m%s\033[92m",
2: "\u001b[33;1m%s\033[92m",
3: "\033[94m%s\033[92m",
4: "\033[91m%s\033[92m",
5: "\033[91m%s\033[92m",
6: "\033[91m%s\033[92m",
7: "\033[91m%s\033[92m",
}
)
// Scene contains the rows
type Scene struct {
Rows []*Row
}
// NewScene intiniate a new Scene pointer with all the rows already created
func NewScene(nbRows int, nbSeats int) (*Scene, error) {
if nbSeats%nbRows != 0 {
return nil, errors.New("Unbalance seats:rows")
}
nbCols := nbSeats / nbRows
scene := &Scene{}
for i := 0; i < nbRows; i++ {
row := &Row{}
for j := 0; j < nbCols; j++ {
qual := (nbCols / 2) - j
if qual < 0 {
qual = j - (nbCols / 2)
}
row.Seats = append(row.Seats, &Seat{
Quality: qual + i,
Taken: false,
Tag: NewTag(i+1, j+1),
})
}
scene.Rows = append(scene.Rows, row)
}
return scene, nil
}
// ReserveN will try to find N seats available by quality
func (s *Scene) ReserveN(nbSeats int) (string, error) {
if nbSeats >= 10 {
return "", errors.New("Not Available")
}
var bestSeats []*Seat
bs := math.MaxInt32
for _, r := range s.Rows {
ss, err := r.Fit(nbSeats)
if err != nil || len(ss) <= 0 {
continue
}
if ns := Score(ss); ns < bs {
bestSeats = ss
bs = ns
}
}
for _, s := range bestSeats {
if err := s.Reserve(); err != nil {
return "", err
}
}
if len(bestSeats) <= 0 {
return "", fmt.Errorf("can't find %d available seats", nbSeats)
}
return rangeStr(bestSeats), nil
}
// Reserve is used to reserve specific seats
func (s *Scene) Reserve(strs ...string) error {
for _, str := range strs {
r, c, err := ParseTag(str)
if err != nil {
return err
}
if err := s.Rows[r-1].Seats[c-1].Reserve(); err != nil {
return err
}
}
return nil
}
// String is used to implement the String inteface and be able to pretty print scene struct
func (s *Scene) String() string {
var str string
for _, r := range s.Rows {
str = fmt.Sprintf("%s%v\n", str, r)
}
return str
}
// Row contains the seats by rows
type Row struct {
Seats []*Seat
}
// Fit try to find the best match for the number of seats needed
func (r *Row) Fit(n int) ([]*Seat, error) {
if n > len(r.Seats) {
return nil, errors.New("row don't have enough seats")
}
bms := math.MaxInt16
bh, bt := 0, 0
h, t := 0, 0
for i := 0; i < len(r.Seats); i++ {
h, t = i, i
if r.Seats[i].Taken {
continue
}
for j := i; j < len(r.Seats); j++ {
if !r.Seats[j].Taken {
t++
} else {
break
}
if t-h == n {
if score := Score(r.Seats[h:t]); score < bms {
bms = score
bh = h
bt = t
}
}
}
}
return r.Seats[bh:bt], nil
}
// String is used to implement the String inteface and be able to pretty print Row struct
func (r *Row) String() string {
var str string
for _, s := range r.Seats {
str = fmt.Sprintf("%s%s\t", str, s.String())
}
return str
}
// Seat contains the Tag "ex : R4C2", the Quality and if it's already taken
type Seat struct {
Tag string
Quality int
Taken bool
}
// Reserve sets the values for the seat so that it's consider reserved
func (s *Seat) Reserve() error {
if !s.Taken {
s.Taken = true
s.Quality = -1
return nil
}
return errors.New("seat already taken")
}
// String is used to implement the String inteface and be able to pretty print seat struct
func (s *Seat) String() string {
return fmt.Sprintf(Qualities[s.Quality], s.Tag)
}
// NewTag is use to generate a tag based on the row and col
func NewTag(row int, col int) string {
return fmt.Sprintf("R%dC%d", row, col)
}
// ParseTag returns the values for the row and column in the tag
func ParseTag(tag string) (row int, col int, err error) {
reg := regexp.MustCompile("[0-9]+")
v := reg.FindAllString(tag, -1)
r, err := strconv.Atoi(v[0])
if err != nil {
return 0, 0, errors.New("invalid row number")
}
c, err := strconv.Atoi(v[1])
if err != nil {
return 0, 0, errors.New("invalid col number")
}
return r, c, nil
}
// Score calculate the quality score for an array of seats
func Score(seats []*Seat) int {
score := 0
for _, s := range seats {
score += s.Quality
}
return score
}
// rangeStr is used to print the seats range found
func rangeStr(ss []*Seat) string {
if len(ss) == 1 {
return fmt.Sprintf("%s", ss[0].Tag)
}
return fmt.Sprintf("%s - %s", ss[0].Tag, ss[len(ss)-1].Tag)
}
func main() {
scene, err := NewScene(3, 33)
if err != nil {
panic(err)
}
err = scene.Reserve(os.Args[1:]...)
if err != nil {
panic(err)
}
fmt.Println(scene)
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("Number of Seats >> ")
scanner.Scan()
in := scanner.Text()
if len(in) != 0 {
n, err := strconv.Atoi(in)
if err != nil {
fmt.Println("Invalid input")
continue
}
r, err := scene.ReserveN(n)
if err != nil {
fmt.Println(err)
continue
}
fmt.Printf("%s reserved\n", r)
fmt.Println(scene)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment