nquest/pkg/service/engine.go

123 lines
2.5 KiB
Go

package service
import (
"context"
"errors"
"strings"
"github.com/jackc/pgx/v5/pgconn"
"gitrepo.ru/neonxp/nquest/pkg/models"
"gorm.io/gorm"
)
var (
ErrGameNotStarted = errors.New("game not started")
ErrInvalidCode = errors.New("invalid code")
ErrOldCode = errors.New("old code")
ErrGameFinished = errors.New("game finished")
)
type Engine struct {
DB *gorm.DB
}
func NewEngine(db *gorm.DB) *Engine {
return &Engine{
DB: db,
}
}
func (e *Engine) GetState(ctx context.Context, game *models.Game, user *models.User) (*models.GameCursor, error) {
db := e.DB.WithContext(ctx)
// Пытаемся получить GamePassing
cursor := &models.GameCursor{
User: user,
Game: game,
Task: game.Tasks[0],
Status: models.TaskStarted,
Codes: []*models.Code{},
}
err := db.
Where(`user_id = ? and game_id = ? and status = ?`, user.ID, game.ID, models.TaskStarted).
Preload("Task").
Preload("Task.Codes").
Preload("Task.Next").
Preload("Task.Next.Codes").
Preload("Codes").
FirstOrCreate(cursor).
Error
if err != nil {
if err, ok := err.(*pgconn.PgError); ok {
if err.Code == "23505" {
return nil, ErrGameNotStarted
}
}
return nil, err
}
return cursor, nil
}
func (e *Engine) EnterCode(ctx context.Context, game *models.Game, user *models.User, code string) (*models.GameCursor, error) {
db := e.DB.WithContext(ctx)
st, err := e.GetState(ctx, game, user)
if err != nil {
return nil, err
}
code = strings.Trim(code, " \n\t")
code = strings.ToLower(code)
var currentCode *models.Code
for _, c := range st.Task.Codes {
if c.Code == code {
currentCode = c
break
}
}
if currentCode == nil {
return nil, ErrInvalidCode
}
for _, c := range st.Codes {
if c.ID == currentCode.ID {
return nil, ErrOldCode
}
}
st.Codes = append(st.Codes, currentCode)
if err := db.Save(st).Error; err != nil {
return nil, err
}
if len(st.Codes) != len(st.Task.Codes) {
return st, nil
}
// Уровень пройден. Выдаем следующий
st.Status = models.TaskFinished
if err := db.Save(st).Error; err != nil {
return nil, err
}
if st.Task.Next == nil {
user.Experience += st.Game.Points
if err := db.Save(user).Error; err != nil {
return nil, err
}
return nil, ErrGameFinished
}
newState := &models.GameCursor{
User: user,
Game: game,
Task: st.Task.Next,
Status: models.TaskStarted,
Codes: []*models.Code{},
}
return newState, db.Create(newState).Error
}