diff --git a/question/prompts/q01.md b/question/prompts/q01.md new file mode 100644 index 0000000..16737fc --- /dev/null +++ b/question/prompts/q01.md @@ -0,0 +1,91 @@ +# No Time For Directions! + +**Hermes** is the Greek god, and amongst others, he is the god of travel, trade, and athletes. +Hermes has placed his hope in you. + +You're dropped to a random location near Mount Cyllene in Arcadia, widely considered the birthplace +of Hermes. After years of training, **You** are now set out on a quest. You must steal a key from +**Hecate**, hidden near Mount Cyllene. + +Unfortunately, "near", is as close as you know where you are. The instructions on the parchment +Hermes gave begin from here, however, he never had the time to tell you how to follow them, or where +they lead to. + +The document has different markings that appear to tell you which direction to travel in. They +indicate a direction, (`N`, `S`, `E` or `W`), and the number of steps you must take to find the +hiding location. + +The problem is, that there's over a 100 different directions, and there's no time following these +directions one by one. It will take far too long! You take a moment and work out the final +destination, so you can get more quickly. Given that you can only walk in the cardinal directions, +what is the shortest path to the destination? + +### Example + +- Given the following input + + +``` +N5 +E2 +S9 +W3 +``` + + Instructs you to to travel `5` steps North, `2` steps East, `9` steps South, + and `3` steps West. Simplifying it, means `4` steps South, and `1` step West, + or `5` steps away. + +- Given the following input + +``` +N6 +E5 +N5 +W3 +N4 +S9 +E4 +S1 +W6 +E3 +``` + + Leaves you `5` steps North, and `3` steps East, or `8` steps away. + +Each line will have **at least 2** characters of input, the first being the direction, and +the second being the number of steps. The number of steps on each line will +always be between 1 and 9, inclusive, steps. + +**How many steps away** is the key? + +{{ if eq .Part 2 }} + +**Congratulations! You got Part 1 correct. Your answer was `{{ .Answer1 }}`.** + +## Part 2 + +After some more inspection of the instructions, you decipher the final clue. The final location of +the steps lead you to a different location. However, the first location you visit **twice** is the +location where the key is hidden. + +### Example + +Given the following input: + +``` +S7 +W9 +E4 +N4 +S3 +E5 +S1 +``` + +The first location visit twice is `7` blocks away South. + +With these new instructions, to find the key, **how many steps away is the first location you visit +twice?** + +{{ end }} diff --git a/question/prompts/q_01.md.tmpl b/question/prompts/q_01.md.tmpl deleted file mode 100644 index 47d3323..0000000 --- a/question/prompts/q_01.md.tmpl +++ /dev/null @@ -1,67 +0,0 @@ -# No Time For Directions! - -__Hermes__ is the Greek god, and amongst others, he is the god of travel, trade, -and athletes. Hermes has placed his hope in you. - -You're dropped to a random location near Mount Cyllene in Arcadia, widely -considered the birthplace of Hermes. After years of training, **You** are now -set out on a quest. You must steal a key from __Hecate__, hidden near Mount -Cyllene. - -Unfortunately, "near", is as close as you know where you are. The instructions -on the parchment Hermes gave begin from here, however, he never had the time to -tell you how to follow them, or where they lead to. - -The document has different markings that appear to tell you which direction to -travel in. They indicade a direction, (`N`, `S`, `E` or `W`), and the number of -steps you must take to find the hiding location. - -The problem is, that there's over a 100 different directions, and there's no -time following these directions one by one. It will take far too long! You take -a moment and work out the final destination, so you can get more quickly. Given -that you can only walk in the cardinal directions, what is the shortest path to -the destination? - -### Example - -- Given the following input - - ``` - N5 - E2 - S9 - W3 - ``` - - Instructs you to to travel `5` steps North, `2` steps East, `9` steps South, - and `3` steps West. Simplifying it, means `4` steps South, and `1` step West, - or `5` steps away. - -- Given the following input - - ``` - N6 - E5 - N5 - W3 - N4 - S9 - E4 - S1 - W6 - E3 - ``` - - Leaves you `5` steps North, and `3` steps East, or `8` steps away. - -Each line will have 2 characters of input, the first being the direction, and -the second being the number of steps. The number of steps on each line will -always be between 1 and 9, inclusive, steps. - -**How many steps away** is the key? - -{{ if ge .Step 2 }} - -## Part 2 - -{{ end }} diff --git a/question/q01.go b/question/q01.go new file mode 100644 index 0000000..f836346 --- /dev/null +++ b/question/q01.go @@ -0,0 +1,150 @@ +package question + +import ( + _ "embed" + "fmt" + "math" + "math/rand" + "strconv" + "strings" + + "github.com/hhhapz/codequest/models" +) + +func move(x, y int, dir byte, step int) (int, int) { + switch dir { + case 'N': + return x, y + step + case 'S': + return x, y - step + case 'E': + return x + step, y + case 'W': + return x - step, y + } + return x, y +} + +func q1P1(steps []string) (x int, y int) { + for _, step := range steps { + dir := step[0] + amt, _ := strconv.Atoi(step[1:]) + x, y = move(x, y, dir, amt) + } + return +} + +func q1P2(steps []string) (x int, y int) { + known := make(map[int]map[int]bool) + + for _, step := range steps { + dir := step[0] + amt, _ := strconv.Atoi(step[1:]) + x, y = move(x, y, dir, amt) + + if known[x] == nil { + known[x] = make(map[int]bool) + } + if known[x][y] { + return + } + known[x][y] = true + } + return +} + +func q1Total(a, b int) int { + if a < 0 { + a = -a + } + if b < 0 { + b = -b + } + return a + b +} + +func init() { + const directions = "NSWE" + var q *Question + q = &Question{ + ID: "directions", + Text: q01Text, + Level: Level1, + Generate: func(u *models.User) string { + res := make([]string, 0, 100) + + r := userRandom(u) + + known := make(map[int]map[int]bool) + var x, y int + for i := 0; i < 100; i++ { + dir := directions[r.Intn(4)] + steps := r.Intn(30) + 1 + newX, newY := move(x, y, dir, steps) + + if known[newX] == nil { + known[newX] = make(map[int]bool) + } + if known[newX][newY] { + i-- + continue + } + + known[newX][newY] = true + x, y = newX, newY + + res = append(res, fmt.Sprintf("%c%d", dir, steps)) + } + + n := rand.Intn(20) + 10 + locX, locY := q1P1(res[:n]) + + dup := rand.Intn(20) + 35 + lastX, lastY := q1P1(res[:dup]) + + fmt.Println(locX, locY) + fmt.Println(lastX, lastY) + fmt.Println(dup) + + dX := math.Max(float64(locX), float64(lastX)) - math.Min(float64(locX), float64(lastX)) + if locX > lastX { + res[dup] = fmt.Sprintf("E%d", int(dX)) + dup++ + } else if locX < lastX { + res[dup] = fmt.Sprintf("W%d", int(dX)) + dup++ + } + + fmt.Println(res[dup-1]) + fmt.Println("!!") + + if locY > lastY { + res[dup] = fmt.Sprintf("N%d", int(math.Max(float64(locY), float64(lastY))-math.Min(float64(locY), float64(lastY)))) + } else if locY < lastY { + res[dup] = fmt.Sprintf("S%d", int(math.Max(float64(locY), float64(lastY))-math.Min(float64(locY), float64(lastY)))) + } + + return strings.Join(res, "\n") + }, + Validate: func(u *models.User, level Part, input string) bool { + inp := q.Generate(u) + + lastX, lastY := q1P1(strings.Split(inp, "\n")) + if level == Part1 { + total := q1Total(lastX, lastY) + return strconv.Itoa(total) == input + } + lastX, lastY = q1P2(strings.Split(inp, "\n")) + if level == Part2 { + total := q1Total(lastX, lastY) + return strconv.Itoa(total) == input + } + return false + }, + } + + Register(q) +} + +//go:embed prompts/q01.md +var q01Text string diff --git a/question/q01_test.go b/question/q01_test.go new file mode 100644 index 0000000..7c02db0 --- /dev/null +++ b/question/q01_test.go @@ -0,0 +1,29 @@ +package question + +import ( + "fmt" + "strings" + "testing" + + "github.com/hhhapz/codequest/models" +) + +func TestQ01(t *testing.T) { + u := &models.User{ + ID: "0", + } + + q := QuestionByID("directions") + + input := q.Generate(u) + + fmt.Println("STAAART") + x, y := q1P1(strings.Split(input, "\n")) + fmt.Println("STAAART") + + t.Logf("SOLUTION (P1): (%d, %d): %d", x, y, q1Total(x, y)) + x, y = q1P2(strings.Split(input, "\n")) + t.Logf("SOLUTION (P2): (%d, %d): %d", x, y, q1Total(x, y)) + + fmt.Printf("INPUT: %v", input) +} diff --git a/question/q02.go b/question/q02.go new file mode 100644 index 0000000..dbb1a3a --- /dev/null +++ b/question/q02.go @@ -0,0 +1,11 @@ +package question + +func init() { + Register(&Question{ + ID: "", + Text: "", + Level: 0, + Generate: nil, + Validate: nil, + }) +} diff --git a/question/q03.go b/question/q03.go new file mode 100644 index 0000000..9eac1f3 --- /dev/null +++ b/question/q03.go @@ -0,0 +1 @@ +package question diff --git a/question/q_01.go b/question/q_01.go deleted file mode 100644 index c69e8b8..0000000 --- a/question/q_01.go +++ /dev/null @@ -1,13 +0,0 @@ -package question - -import "github.com/hhhapz/codequest/models" - -func init() { - Register(&Question{ - ID: "directions", - Text: ``, - Level: 0, - Generate: func(*models.User) string { panic("not implemented") }, - Validate: func(*models.User, string) bool { panic("not implemented") }, - }) -} diff --git a/question/q_02.go b/question/q_02.go deleted file mode 100644 index ee393d0..0000000 --- a/question/q_02.go +++ /dev/null @@ -1,13 +0,0 @@ -package question - -import "github.com/hhhapz/codequest/models" - -func init() { - Register(&Question{ - ID: "", - Text: "", - Level: 0, - Generate: func(*models.User) string { panic("not implemented") }, - Validate: func(*models.User, string) bool { panic("not implemented") }, - }) -} diff --git a/question/q_03.go b/question/q_03.go deleted file mode 100644 index e69de29..0000000 diff --git a/question/question.go b/question/question.go index a9590a9..f9b0316 100644 --- a/question/question.go +++ b/question/question.go @@ -2,6 +2,7 @@ package question import ( "math/rand" + "strconv" "github.com/hhhapz/codequest/models" ) @@ -13,21 +14,24 @@ var bank Bank // // A custom type was created for convenience for picking random questions based // on difficulties, and for registration. -type Bank [][]*Question +type Bank []*Question func Register(q *Question) { - bank[q.Level] = append(bank[q.Level], q) + bank = append(bank, q) } -func Questions(user *models.User, level Level) []*Question { - qs := make([]*Question, len(bank)) - r := rand.New(rand.NewSource(user.CreatedAt.Time().Unix())) - for level := range bank { - idx := r.Intn(len(bank[level])) - qs[level] = bank[level][idx] +func QuestionByID(id string) *Question { + for _, q := range bank { + if q.ID == id { + return q + } } + return nil +} - return qs +func Questions(user *models.User, level Level) []*Question { + // TODO: player skill level should be used to determine which problems to return + return bank } type Question struct { @@ -36,16 +40,29 @@ type Question struct { Level Level Generate func(user *models.User) string - Validate func(user *models.User, solution string) bool + Validate func(user *models.User, part Part, solution string) bool } -// Level represents the difficulty of each question. -// As the level gets higher, the difficulty also gets higher. type Level int -// Allowed difficulty levels. const ( Level1 Level = iota Level2 - Level3 ) + +type Part int + +const ( + Part1 Part = iota + Part2 +) + +func userRandom(u *models.User) *rand.Rand { + id := u.ID + if len(id) > 17 { + id = id[:17] + } + seed, _ := strconv.ParseInt(id, 10, 64) + + return rand.New(rand.NewSource(seed)) +} diff --git a/schema/schema.yaml b/schema/schema.yaml deleted file mode 100644 index 991b97a..0000000 --- a/schema/schema.yaml +++ /dev/null @@ -1,298 +0,0 @@ -openapi: "3.0.0" -info: - version: 0.1.0 - title: Swagger Hackathon - description: A hackathon, hosted in JIS API specification created by Hamza Ali. - contact: - name: Hamza Ali - email: me@hamzantal.pw - license: - name: MIT License - url: "https://hamza.mit-license.org/" - -paths: - /auth/code: - get: - description: Generate oauth exchange url. - tags: ["Auth"] - operationId: gen oauth - parameters: - - name: callback - in: query - required: true - schema: - type: string - format: uri - responses: - '200': - description: OAuth Consent Page URI. - content: - application/json: - schema: - $ref: "#/components/schemas/ConsentPage" - default: - $ref: "#/components/responses/DefaultResponse" - - /auth/authorize: - get: - description: Authorization response callback location. - tags: ["Auth"] - operationId: authorize callback - parameters: - - name: state - in: query - required: true - schema: - type: string - - name: code - in: query - required: true - schema: - type: string - responses: - '302': - description: Redirect to webpage. - headers: - Location: - schema: - type: string - format: uri - default: - $ref: "#/components/responses/DefaultResponse" - - /auth/token: - delete: - tags: ["Auth"] - operationId: delete token - x-go-middlewares: ["token"] - parameters: - - $ref: "#/components/parameters/Token" - - name: all - in: query - required: true - schema: - type: boolean - responses: - '204': - description: User successfully logged out. - default: - $ref: "#/components/responses/DefaultResponse" - - /users/me: - get: - description: Get self user information. - tags: ["Users"] - operationId: get me - x-go-middlewares: ["token"] - parameters: - - $ref: "#/components/parameters/Token" - responses: - '200': - description: User information. - content: - application/json: - schema: - $ref: "#/components/schemas/User" - default: - $ref: "#/components/responses/DefaultResponse" - put: - description: Update self user. - tags: ["Users"] - operationId: modify user - x-go-middlewares: ["token"] - parameters: - - $ref: "#/components/parameters/Token" - requestBody: - description: Modified user information. - content: - application/json: - schema: - type: object - required: - - name - - grade_level - properties: - name: - type: string - grade_level: - type: integer - responses: - '200': - description: New user data - content: - application/json: - schema: - $ref: "#/components/schemas/User" - default: - $ref: "#/components/responses/DefaultResponse" - - /users/email: - get: - description: |- - Get user info by email. - Requires admin to get user info not equal to the owner of the token. - tags: ["Users"] - operationId: get user by email - x-go-middlewares: ["token"] - parameters: - - $ref: "#/components/parameters/Token" - - name: email - in: query - description: User email. - required: true - schema: - type: string - format: email - responses: - '200': - description: User information. - content: - application/json: - schema: - $ref: "#/components/schemas/User" - default: - $ref: "#/components/responses/DefaultResponse" - put: - description: Update another user. Requires admin. - tags: ["Users"] - operationId: modify other user - x-go-middlewares: ["token", "admin_token"] - parameters: - - name: token - in: cookie - description: User authentication token. - required: true - schema: - type: string - - name: email - in: query - description: User email. - required: true - schema: - type: string - format: email - requestBody: - description: Modified user information. - content: - application/json: - schema: - type: object - required: - - name - - email - - picture - - grade_level - - teacher - - admin - properties: - name: - type: string - new_email: - type: string - format: email - picture: - type: string - format: uri - grade_level: - type: integer - teacher: - type: boolean - admin: - type: boolean - - responses: - '200': - description: User information. - content: - application/json: - schema: - $ref: "#/components/schemas/User" - default: - $ref: "#/components/responses/DefaultResponse" - - /users/all: - get: - description: Get all users. Requires admin. - tags: ["Users"] - operationId: get all users - x-go-middlewares: ["token", "admin_token"] - parameters: - - $ref: "#/components/parameters/Token" - responses: - '200': - description: All user information. - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/User" - default: - $ref: "#/components/responses/DefaultResponse" - -components: - parameters: - Token: - name: token - in: cookie - description: User authentication token. - required: true - schema: - type: string - - responses: - DefaultResponse: - description: Unexpected server error or invalid user input. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - schemas: - ConsentPage: - type: object - required: - - url - properties: - url: - type: string - format: uri - User: - type: object - required: - - id - - name - - email - - picture - - teacher - - admin - - created_at - properties: - id: - type: string - name: - type: string - email: - type: string - format: email - picture: - type: string - format: uri - grade_level: - type: integer - description: GradeLevel is only present if teacher is false. - teacher: - type: boolean - admin: - type: boolean - created_at: - type: string - format: date-time - - Error: - type: object - required: - - message - properties: - message: - type: string