Skip to content

Commit d386360

Browse files
authored
Lucky tickets (#171)
* Add the lucky-tickets hole. Note that the algorithm used in lucky-tickets.go is not the most efficient algorithm, but it is only used to generate the results for the random test cases and typically runs in less than 20 milliseconds. For the longest test case, it took about 2.5 seconds to generate the result on my machine, so I am including the precomputed result for performance. For many languages, a better algorithm will be required to solve the hole within the time constraint. * hole/hole.go: Reduce complexity of the Play function.
1 parent b1273c5 commit d386360

File tree

4 files changed

+120
-16
lines changed

4 files changed

+120
-16
lines changed

db/0.schema.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CREATE TYPE hole AS ENUM (
44
'12-days-of-christmas', '99-bottles-of-beer', 'abundant-numbers',
55
'arabic-to-roman', 'brainfuck', 'christmas-trees', 'cubes', 'diamonds',
66
'divisors', 'emirp-numbers', 'evil-numbers', 'fibonacci', 'fizz-buzz',
7-
'happy-numbers', 'leap-years', 'morse-decoder', 'morse-encoder',
7+
'happy-numbers', 'leap-years', 'lucky-tickets', 'morse-decoder', 'morse-encoder',
88
'niven-numbers', 'odious-numbers', 'ordinal-numbers', 'pangram-grep',
99
'pascals-triangle', 'pernicious-numbers', 'poker', 'prime-numbers',
1010
'quine', 'rock-paper-scissors-spock-lizard', 'roman-to-arabic',

hole/hole.go

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,37 +21,46 @@ type Scorecard struct {
2121
Took time.Duration
2222
}
2323

24-
func Play(ctx context.Context, holeID, langID, code string) (score Scorecard) {
24+
func getAnswer(holeID, code string) ([]string, string) {
25+
var answer string
26+
var args []string
2527
switch holeID {
2628
case "arabic-to-roman", "roman-to-arabic":
27-
score.Args, score.Answer = arabicToRoman(holeID == "roman-to-arabic")
29+
args, answer = arabicToRoman(holeID == "roman-to-arabic")
2830
case "brainfuck":
29-
score.Args, score.Answer = brainfuck()
31+
args, answer = brainfuck()
32+
case "lucky-tickets":
33+
args, answer = luckyTickets()
3034
case "morse-decoder", "morse-encoder":
31-
score.Args, score.Answer = morse(holeID == "morse-decoder")
35+
args, answer = morse(holeID == "morse-decoder")
3236
case "ordinal-numbers":
33-
score.Args, score.Answer = ordinalNumbers()
37+
args, answer = ordinalNumbers()
3438
case "pangram-grep":
35-
score.Args, score.Answer = pangramGrep()
39+
args, answer = pangramGrep()
3640
case "poker":
37-
score.Args, score.Answer = poker()
41+
args, answer = poker()
3842
case "quine":
39-
score.Answer = code
43+
answer = code
4044
case "rock-paper-scissors-spock-lizard":
41-
score.Args, score.Answer = rockPaperScissorsSpockLizard()
45+
args, answer = rockPaperScissorsSpockLizard()
4246
case "seven-segment":
43-
score.Args, score.Answer = sevenSegment()
47+
args, answer = sevenSegment()
4448
case "spelling-numbers":
45-
score.Args, score.Answer = spellingNumbers()
49+
args, answer = spellingNumbers()
4650
case "sudoku":
47-
score.Args, score.Answer = sudoku()
51+
args, answer = sudoku()
4852
case "ten-pin-bowling":
49-
score.Args, score.Answer = tenPinBowling()
53+
args, answer = tenPinBowling()
5054
case "united-states":
51-
score.Args, score.Answer = unitedStates()
55+
args, answer = unitedStates()
5256
default:
53-
score.Answer = answers[holeID]
57+
answer = answers[holeID]
5458
}
59+
return args, answer
60+
}
61+
62+
func Play(ctx context.Context, holeID, langID, code string) (score Scorecard) {
63+
score.Args, score.Answer = getAnswer(holeID, code)
5564

5665
var stderr, stdout bytes.Buffer
5766

hole/lucky-tickets.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package hole
2+
3+
import (
4+
"math/rand"
5+
"strconv"
6+
"strings"
7+
)
8+
9+
type Ticket struct {
10+
digits int
11+
base int
12+
result int64
13+
}
14+
15+
var data = []Ticket{
16+
{8, 2, 70},
17+
{4, 8, 344},
18+
{2, 10, 10},
19+
{4, 10, 670},
20+
{6, 10, 55252},
21+
{14, 12, 39222848622984},
22+
}
23+
24+
func iPow(a, b int64) int64 {
25+
var result int64 = 1
26+
for b != 0 {
27+
if b&1 != 0 {
28+
result *= a
29+
}
30+
b >>= 1
31+
a *= a
32+
}
33+
return result
34+
}
35+
36+
func sumDigits(number int64, base int64) int64 {
37+
var result int64
38+
for number > 0 {
39+
result += number % base
40+
number /= base
41+
}
42+
return result
43+
}
44+
45+
func luckyTickets() ([]string, string) {
46+
tickets := make([]Ticket, len(data))
47+
copy(tickets, data)
48+
49+
// Randomly generate additional test cases.
50+
for i := 0; i < 5; i++ {
51+
digits := 2 + 2*rand.Intn(5)
52+
base := 2 + rand.Intn(15)
53+
54+
halfValue := iPow(int64(base), int64(digits/2))
55+
maxSum := (base - 1) * digits / 2
56+
counts := make([]int64, maxSum+1)
57+
var j int64
58+
for ; j < halfValue; j++ {
59+
counts[sumDigits(j, int64(base))] += 1
60+
}
61+
62+
var result int64
63+
for _, count := range counts {
64+
result += count * count
65+
}
66+
67+
tickets = append(tickets, Ticket{digits, base, result})
68+
}
69+
70+
args := make([]string, len(tickets))
71+
outs := make([]string, len(tickets))
72+
73+
for i, item := range tickets {
74+
args[i] = strconv.Itoa(item.digits) + " " + strconv.Itoa(item.base)
75+
outs[i] = strconv.FormatInt(item.result, 10)
76+
}
77+
78+
// Shuffle
79+
for i := range args {
80+
j := rand.Intn(i + 1)
81+
args[i], args[j] = args[j], args[i]
82+
outs[i], outs[j] = outs[j], outs[i]
83+
}
84+
85+
return args, strings.Join(outs, "\n")
86+
}

holes.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,15 @@ preamble = '''
358358
to and including <b>2400</b>.
359359
'''
360360

361+
['Lucky Tickets']
362+
category = 'Mathematics'
363+
preamble = '''
364+
<p>
365+
In Russia, bus tickets numbers consist of 6 decimal digits. It is considered lucky when the sum of the first three digits equals the sum of the last three digits. The concept of lucky tickets can be extended to ticket numbering systems with even numbers of digits and arbitrary bases.
366+
<p>
367+
Each argument describes a ticket numbering system and consists of two numbers separated by a space. The first is the even number of digits. The second is the base of the numbering system (2-16). For each argument, output the total number of lucky tickets for the numbering system on a separate line.
368+
'''
369+
361370
['Morse Decoder']
362371
category = 'Transform'
363372
preamble = '''

0 commit comments

Comments
 (0)