package story_service import ( "context" "encoding/json" "fmt" "log" "os" "regexp" "strings" ) var ( replaceMap = map[string]string{ "a": "а", "e": "е", "o": "о", "c": "с", "p": "р", "x": "х", "y": "у", "k": "к", "m": "м", "t": "т", "h": "н", "b": "в", "u": "и", } ) type Story struct { Places []*Place `json:"places"` } type Place struct { Code string `json:"code"` Name string `json:"name"` Text string `json:"text"` Applications []*Application `json:"applications"` } type Graph struct { Nodes []*Node Edges []*Edge } type Node struct { ID int32 Label string } type Edge struct { From int32 To int32 } type Application struct { Name string `json:"name"` } type StoryService struct { story *Story } func NewStoryService(filepath string) (*StoryService, error) { data, err := os.ReadFile(filepath) if err != nil { return nil, fmt.Errorf("story file %s not found", filepath) } log.Printf("load story from: %s", filepath) story := &Story{} if err := json.Unmarshal(data, story); err != nil { return nil, err } return &StoryService{story: story}, nil } func (s *StoryService) GetPlace(code string) *Place { if strings.HasPrefix(code, "[") || strings.HasSuffix(code, "]") { return &Place{ Code: code, Name: "Не найдено", Text: "Уважаемые детективы внимательно прочитайте правила.", } } code = clearCode(code) for _, place := range s.story.Places { if clearCode(place.Code) == code { re := regexp.MustCompile(`\(\[[a-zA-Zа-яА-Я\d-]+\]\)`) place.Text = re.ReplaceAllString(place.Text, "") return place } } return &Place{ Code: code, Name: "Не найдено", Text: "Такой точки не существует.", } } func (s *StoryService) GetGraph(ctx context.Context) *Graph { m := make(map[string]int32, len(s.story.Places)) nodes := make([]*Node, 0, len(s.story.Places)) for i, place := range s.story.Places { m[clearCode(place.Code)] = int32(i) nodes = append(nodes, &Node{ID: int32(i), Label: place.Code}) } edges := make([]*Edge, 0, len(s.story.Places)*3) for _, place := range s.story.Places { re := regexp.MustCompile(`\(\[[a-zA-Zа-яА-Я\d-]+\]\)`) matches := re.FindAllString(place.Text, -1) for _, match := range matches { edges = append(edges, &Edge{ From: m[clearCode(place.Code)], To: m[clearMatch(match)], }) } } return &Graph{ Nodes: nodes, Edges: edges, } } func clearMatch(s string) string { s = strings.TrimPrefix(s, "(") s = strings.TrimPrefix(s, "[") s = strings.TrimSuffix(s, ")") s = strings.TrimSuffix(s, "]") return clearCode(s) } func clearCode(code string) string { code = strings.ToLower(code) code = strings.TrimSpace(code) code = strings.ReplaceAll(code, "-", "") for latin, cyrillic := range replaceMap { code = strings.ReplaceAll(code, latin, cyrillic) } return code }