FizzBuzz Reverse Solver

32

Synopsis: Given the output of a generalised FizzBuzz program, return the list of factors and words used for the program.

Challenge Description

Imagine a generalised FizzBuzz program that takes in as input a list of factors and words to use and the number to start from. For instance, if the input of this program was

3 2,Ninja 5,Bear 7,Monkey

The program would print out numbers from 3 to 100, replacing numbers divisible by 2 with Ninja, numbers divisible by 5 with Bear, and numbers divisible by 7 with Monkey. For numbers that are divisible then more than one of those terms, the program will concatenate the words, printing things such as NinjaBear or BearMonkey or NinjaMonkey or NinjaBearMonkey. Here's the output of that input:

3
Ninja
Bear
Ninja
Monkey
Ninja
9
NinjaBear
11
Ninja
13
NinjaMonkey
Bear
Ninja
17
Ninja
19
NinjaBear
Monkey
Ninja
23
Ninja
Bear
Ninja
27
NinjaMonkey
29
NinjaBear
31
Ninja
33
Ninja
BearMonkey
Ninja
37
Ninja
39
NinjaBear
41
NinjaMonkey
43
Ninja
Bear
Ninja
47
Ninja
Monkey
NinjaBear
51
Ninja
53
Ninja
Bear
NinjaMonkey
57
Ninja
59
NinjaBear
61
Ninja
Monkey
Ninja
Bear
Ninja
67
Ninja
69
NinjaBearMonkey
71
Ninja
73
Ninja
Bear
Ninja
Monkey
Ninja
79
NinjaBear
81
Ninja
83
NinjaMonkey
Bear
Ninja
87
Ninja
89
NinjaBear
Monkey
Ninja
93
Ninja
Bear
Ninja
97
NinjaMonkey
99
NinjaBear

Note that whenever the program needs to combine words together, it always goes from lowest number to highest number. So it won't print out something like MonkeyBear (since Monkey is a higher number than Bear is).

Your program should take in the output of the generalised FizzBuzz program as input, and output the input given to the generalised FizzBuzz program. In other words, write a "reverse program" for the generalised FizzBuzz program. For instance, given the code block above as input, your program should output 3 2,Ninja 5,Bear, 7,Monkey.

There are some rules that the words will always follow:

  • It will always be possible to tell exactly what the factors and words are from the input.
  • Each word will begin with a capital letter and will not contain any other capital letters, or numbers.
  • Each factor is unique.

Sample Inputs and Outputs

Input:

Calvins
7
Hobbies
9
10
11
Calvins
13
14
15
Hobbies
17
Calvins
19
20
21
22
23
CalvinsHobbies
25
26
27
28
29
Calvins
31
Hobbies
33
34
35
Calvins
37
38
39
Hobbies
41
Calvins
43
44
45
46
47
CalvinsHobbies
49
50
51
52
53
Calvins
55
Hobbies
57
58
59
Calvins
61
62
63
Hobbies
65
Calvins
67
68
69
70
71
CalvinsHobbies
73
74
75
76
77
Calvins
79
Hobbies
81
82
83
Calvins
85
86
87
Hobbies
89
Calvins
91
92
93
94
95
CalvinsHobbies
97
98
99
100

Output:

6 6,Calvins 8,Hobbies

Input:

FryEggman
7
Am
Fry
The
11
FryAmEggman
13
14
FryThe
Am
17
FryEggman
19
AmThe
Fry
22
23
FryAmEggman
The
26
Fry
Am
29
FryTheEggman
31
Am
Fry
34
The
FryAmEggman
37
38
Fry
AmThe
41
FryEggman
43
Am
FryThe
46
47
FryAmEggman
49
The
Fry
Am
53
FryEggman
The
Am
Fry
58
59
FryAmTheEggman
61
62
Fry
Am
The
FryEggman
67
Am
Fry
The
71
FryAmEggman
73
74
FryThe
Am
77
FryEggman
79
AmThe
Fry
82
83
FryAmEggman
The
86
Fry
Am
89
FryTheEggman
91
Am
Fry
94
The
FryAmEggman
97
98
Fry
AmThe

Output:

6 3,Fry 4,Am 5,The 6,Eggman

Input:

DeliciousTartApplePie
DeliciousCreamPancakeStrawberry
DeliciousProfiterole
DeliciousCream
DeliciousPancake
DeliciousCreamStrawberryTart

Output:

95 1,Delicious 2,Cream 3,Pancake 4,Strawberry 5,Tart 19,Apple 95,Pie 97,Profiterole

You can get the code I used to generate the inputs here.

absinthe

Posted 2015-07-25T03:16:57.230

Reputation: 8 359

Does the list always go up to exactly 100? – Dennis – 2015-07-25T03:23:12.160

@Dennis Yes, the upper bound is always 100. – absinthe – 2015-07-25T03:24:06.417

15It's just an honor to be in one of your examples. – NinjaBearMonkey – 2015-07-25T03:30:31.003

This is a much better version of your challenge than was originally in the sandbox :) – Beta Decay – 2015-07-25T08:32:22.673

1@NinjaBearMonkey I suppose picking names with many words in them made us better examples. Thanks for including me too @Pyrrha! :) – FryAmTheEggman – 2015-07-25T14:13:25.007

@Pyrrha The last example seems to imply the factors are guaranteed to be unique, but I don't see this in the text. Is that supposed to be the case? – isaacg – 2015-07-25T14:37:15.823

@isaacg Huh, I was sure I had that in there somewhere. Yes, the factors are unique. My apologies for forgetting to include that. I've edited the question to reflect this. – absinthe – 2015-07-25T14:39:01.083

Answers

10

Pyth, 73 bytes

jd+J-101lK.zjL\,Sm,_-F>2+_Jf}d@KTUKd{smtcdf-@dTGUdf>T\:K

That was certaintly a tough one. I think I've covered all the edge cases, including everything in @MartinBüttner's example, and the no repeat factors example.

NinjaBearMonkey, Deliciousness

At the high level, the program first finds all of the words by chopping up the alphabetical strings on capital letters.

Then, the rows are mapped to whether or not each string appears in the row, and each possible factor is tested to see if it produces the same ordering. If it does, the factor is added to a gobal list, which is check to see if the factor was already present. If it was not already present, the factor is used. The strings are ordered by first appearance in the input, which disambiguates the ordering of strings that each appear only once in the same row.

After that, it's just formatting and printing.

isaacg

Posted 2015-07-25T03:16:57.230

Reputation: 39 268

5

Scala, 350 characters

(s:String)⇒{def g(a:Int,b:Int):Int=if(b==0)a.abs else g(b,a%b);val(j,q)=(s.lines:\100→Map.empty[String,Int]){case(l,(n,m))⇒if(l(0).isDigit)(n-1,m)else(n-1,m++(Seq(Seq(l(0)))/:l.tail){case(x,c)⇒if(c.isUpper)Seq(c)+:x else (x(0):+c)+:x.tail}.map{t⇒val w=t.mkString;w→g(m.getOrElse(w,n),n)})};s"${j+1}"+q.map{case(k,v)=>s" $v,$k"}.toSeq.sorted.mkString}

not winning... but nice question.

tested results:

scala> (s:String)⇒{def g(a:Int,b:Int):Int=if(b==0)a.abs else g(b,a%b);val(j,q)=(s.lines:\100→Map.empty[String,Int]){case(l,(n,m))⇒if(l(0).isDigit)(n-1,m)else(n-1,m++(Seq(Seq(l(0)))/:l.tail){case(x,c)⇒if(c.isUpper)Seq(c)+:x else (x(0):+c)+:x.tail}.map{t⇒val w=t.mkString;w→g(m.getOrElse(w,n),n)})};s"${j+1}"+q.map{case(k,v)=>s" $v,$k"}.toSeq.sorted.mkString}
res0: String => String = <function1>

scala> res0("""DeliciousTartApplePie
     | DeliciousCreamPancakeStrawberry
     | DeliciousProfiterole
     | DeliciousCream
     | DeliciousPancake
     | DeliciousCreamStrawberryTart""")
res1: String = 95 1,Delicious 2,Cream 3,Pancake 4,Strawberry 5,Tart 95,Apple 95,Pie 97,Profiterole

scala> res0("""FryEggman
     | 7
     | Am
     | Fry
     | The
     | 11
     | FryAmEggman
     | 13
     | 14
     | FryThe
     | Am
     | 17
     | FryEggman
     | 19
     | AmThe
     | Fry
     | 22
     | 23
     | FryAmEggman
     | The
     | 26
     | Fry
     | Am
     | 29
     | FryTheEggman
     | 31
     | Am
     | Fry
     | 34
     | The
     | FryAmEggman
     | 37
     | 38
     | Fry
     | AmThe
     | 41
     | FryEggman
     | 43
     | Am
     | FryThe
     | 46
     | 47
     | FryAmEggman
     | 49
     | The
     | Fry
     | Am
     | 53
     | FryEggman
     | The
     | Am
     | Fry
     | 58
     | 59
     | FryAmTheEggman
     | 61
     | 62
     | Fry
     | Am
     | The
     | FryEggman
     | 67
     | Am
     | Fry
     | The
     | 71
     | FryAmEggman
     | 73
     | 74
     | FryThe
     | Am
     | 77
     | FryEggman
     | 79
     | AmThe
     | Fry
     | 82
     | 83
     | FryAmEggman
     | The
     | 86
     | Fry
     | Am
     | 89
     | FryTheEggman
     | 91
     | Am
     | Fry
     | 94
     | The
     | FryAmEggman
     | 97
     | 98
     | Fry
     | AmThe""")
res2: String = 6 3,Fry 4,Am 5,The 6,Eggman

gilad hoch

Posted 2015-07-25T03:16:57.230

Reputation: 717

4

Python 2, 366 340 331 bytes

This program receive input via stdin.

New approach:

Calculate the factor of only one occurrence's words by the distance from the end of the line. For example (from the last sample): DeliciousTartApplePie Pie is calculate as: [95,19,5,1][0] and Apple is: [95,19,5,1][1].

import sys
import re
d=[(i,re.findall('[A-Z][a-z]*',l)[::-1])for i,l in enumerate(sys.stdin)]
e=101-len(d)
print e," ".join(`x`+','+`y`[1:-1]for x,y in sorted({next((j-i for j,t in d if j>i and w in t),[x for x in range(i+e,0,-1)if(i+e)%x==0][d[i][1].index(w)]):w for w,i in{w:i for i,l in d[::-1]for w in l}.items()}.iteritems()))

Old approach:

import sys
import re
l=[(i,re.findall('[A-Z][a-z]*',l))for i,l in enumerate(sys.stdin)]
e=101-len(l)
d={}
for i,s in l:
 for w in s[::-1]:
  if w not in d.values():
   d[next((j-i for j,t in l[i+1:]if w in t),next(m for m in range(i+e,0,-1)if(i+e)%m==0and m not in d))]=w 
print e," ".join(`x`+','+`y`[1:-1]for x,y in sorted(d.iteritems()))

Usage:

python FizzBuzzReverseSolver.py < Sample1.txt

Explanation (of Old approach):

  • In general, the program creates a list of line number and list of words (e.g. [(0, []), (1, ['Ninja']), (2, ['Bear']), ...].
  • For each word in every line (starting from the end of the line):
    • Find the next occurrence of the word and insert the difference and the word to a predefined dictionary.
    • If it doesn't found, insert the biggest factor of the line number (including itself) that doesnt already exist in the dictionary and the word to the dictionary.

TheCrypt

Posted 2015-07-25T03:16:57.230

Reputation: 141