This is a simple tool that "translates" the Petscop phonetic button code.
Prounciation data (dictionary.gob
) from The CMU Pronouncing Dictionary.
Word frequency data (frequencies.gob
) from a sample of the Corpus of Contemporary American English.
Buttons -> Phonemes based on /u/LittlestPetscop's post
No automated builds yet.
-
Get Go.
-
Clone the repository (if you don't have Git, you can download a ZIP using the button at the top.) If you clone from the command line, be sure to use
--recursive
or executegit submodule init && git submodule update
after. -
Open up a terminal and
cd
to the resulting folder. -
If you're on Windows, run
set "GOPATH=%cd%"
. Otherwise (OS X, Linux) runexport GOPATH=`pwd`
-
Run
go build petscop_translator.go
. -
Done. You can start it with
petscop_translator
(Windows) or./petscop_translator
.
Each individual button combination must be separated by spaces, and the buttons making up the combination can be separated by any form of punctuation. Surrounding parenthesis are optional.
Example:
$ ./petscop_translator
Loading dictionary...
---
Ask? (L1+X) (R1+TRIANGLE) (L2+X) (R2+SQUARE) (START) (L2+TRIANGLE)
> SIMPLE (exact match)
Ask? (R2+UP) (L2+TRIANGLE) (R1+TRIANGLE) (R2+SQUARE) (L1+X)
> FLIPS (exact match)
Ask? quit
I made this for a few reasons, some more equal than the others:
- To help assist in future translation efforts -- there's no way this code was a one-off given how much detail was obviously put in. A verbose mode
-v
is available to show the phonemes and all possibilities rather than just picking one match, which may be useful for this. - To illustrate a few interesting points about the conversation in Petscop 11.
- (most important) To demonstrate that it is in fact possible for a program to do this automatically. While I agree that it takes some suspension of belief for a PS1 game to be capable of this, given the memory consumption of the dictionary (about 5 MB uncompressed, which is more RAM than the PS1 has. Streaming from disk is possible, but would be very slow), a PC game can obviously do this. Not counting the stemmer which is an external library, this proof of concept is less than 500 lines of Go.
Here's the output of the program running on the transcript of the Petscop 11 conversation:
Ask? (L2+DOWN) (LEFT) (L2+TRIANGLE) (R1+UP)
> HELLO (exact match)
Ask? (R2+SQUARE) (TRIANGLE) (L2+TRIANGLE)
> PALL (exact match)
Ask? (L2+DOWN) (LEFT) (L2+TRIANGLE)
> HELL (exact match)
Ask? (L2+DOWN) (LEFT) (L2+TRIANGLE) (R1+UP)
> HELLO (exact match)
Ask? (R2+UP) (L2+LEFT) (L2+SQUARE) (R1+CIRCLE)
> FUNNY (exact match)
Ask? (L2+DOWN) (X)
> HA (exact match)
Ask? (L2+DOWN) (X)
> HA (exact match)
Ask? (R2+SQUARE) (L2+TRIANGLE) (R1+X)
> PLAY (exact match)
Ask? (L2+X) (R1+CIRCLE) (R2+X) (L1+SQUARE) (R1+TRIANGLE) (L1+DOWN)
> MUSIC (approximate match)
Ask? (R2+UP) (TRIANGLE) (L2+CIRCLE)
> FOR (exact match)
Ask? (R2+TRIANGLE) (R1+X) (R2+TRIANGLE) (R1+CIRCLE)
> BABY (exact match)
Ask? (L1+TRIANGLE) (R1+CIRCLE)
> SHE (exact match)
Ask? (L2+START) (R1+TRIANGLE) (L2+TRIANGLE)
> WILL (exact match)
Ask? (R2+TRIANGLE) (R1+CIRCLE) (L1+DOWN) (START) (L2+X)
> BECOME (exact match)
Ask? (L2+X) (LEFT) (L2+TRIANGLE) (START) (R2+START) (R1+CIRCLE)
> MELODY (exact match)
Ask? (R2+LEFT) (SQUARE) (L2+SQUARE) (L1+DOWN) (L1+X)
> THANKS (approximate match)
Ask? (L2+TRIANGLE) (START) (R2+DOWN) (L2+TRIANGLE) (R1+CIRCLE)
> LOVELY (exact match)
Ask? (L2+TRIANGLE) (START) (R2+DOWN) (L2+TRIANGLE) (R1+CIRCLE)
> LOVELY (exact match)
Ask? (L1+TRIANGLE) (R1+CIRCLE)
> SHE (exact match)
Ask? (L1+START) (L2+CIRCLE) (R1+TRIANGLE) (R2+[SQUARE]) ([R2+CIRCLE])
> TRIPPED (approximate match)
Ask? (R1+X) (L2+SQUARE) (R2+[START])
> AND (approximate match)
Ask? (R2+UP) (LEFT) (L2+TRIANGLE)
> FELL (exact match)
Ask? (R1+X) (L2+SQUARE) (R2+[START])
> AND (approximate match)
Ask? (R1+TRIANGLE) (L1+X)
> IS (exact match)
Ask? (L2+TRIANGLE) (TRIANGLE) (L1+X) (R2+[CIRCLE])
> LOST (exact match)
Ask? (L1+X) (R2+CIRCLE) (X) (R2+SQUARE)
> STOP (exact match)
Ask? (L1+X) (X) (L2+CIRCLE) (R1+CIRCLE)
> SORRY (exact match)
Ask? (R2+CIRCLE) (R1+CIRCLE) (X) (L2+CIRCLE) (START)
> TIARA (exact match)
Ask? (R2+SQUARE) (L2+TRIANGLE) (R1+X) (L1+X)
> PLACE (exact match)
Ask? (R2+TRIANGLE) (SQUARE) (R2+START)
> BAD (exact match)
Ask? (L2+X) (R1+CIRCLE) (R2+X) (L1+SQUARE) (R1+TRIANGLE) (L1+DOWN)
> MUSIC (approximate match)
Ask? (R2+CIRCLE) (R2+X)
> TO (exact match)
Ask? (R2+START) (R2+X)
> DO (exact match)
Ask? (R1+TRIANGLE) (R2+CIRCLE)
> IT (exact match)
Ask? (L2+CIRCLE) (UP) (R2+CIRCLE)
> RIGHT (exact match)
Ask? (L2+SQUARE) (LEFT) (L1+DOWN) (L1+X) (R2+CIRCLE)
> NEXT (exact match)
Ask? (R2+CIRCLE) (UP) (L2+X)
> TIME (exact match)
Ask? (L1+X) (SQUARE) (R2+START)
> SAD (exact match)
Ask? (R1+UP) (L1+DOWN) (R1+X)
> OK (exact match)
Ask? (R2+SQUARE) (TRIANGLE) (L2+TRIANGLE)
> PALL (exact match)
Two things to note:
-
One of the first things that surprised me was that even thought PAUL is in the dictionary, even the earliest version of this program picked PALL just like Petscop itself. In earlier versions of the CMU dictionary, PAUL and PALL are not homonyms: PAUL was
P AO L
, whereas PALL wasP AA L
. However, the latest version has both asP AO L
, and my program selects PALL instead of PAUL because PALL comes first lexicographically. I think this bolsters the odds of Petscop generating these in a similar way, since the same "mistake" was made (presumably) due to PALL coming first. -
The "strange accent" /u/LittlestPetscop noted was a dick. The most noticable words that screwed it up were
AND
(expectedAH N D
, gotEY N D
),BECOME
(expectedB IH K AH M
, gotB IY K AH M
, which is not too unusual of a pronounciation actually),MUSIC
(expectedM Y UW Z IH K
, gotM IY UW Z IH K
), andPLAYS
(expectedP L EY Z
, gotP L EY S
). The program employs fuzzy string matching, so it's able to tolerate 1 phoneme being off. When choosing between potential matches, it goes for the word with the most common stem (at least, of the frequency data I had available which was not much). This, along with allowing buttons like L1+X to be either an S or a Z, fixed most everything except forPLAYS
. Unfortunately,PLACE
is more common thanPLAY
in the data I used (by a tiny amount), and so it picks it. This is the only mistake it makes, and some future improvements could fix this.
Ask? L2+X X L2+CIRCLE R2+DOWN R1+TRIANGLE L2+SQUARE
> MARVIN (exact match)
Ask? L1+X R1+TRIANGLE R2+CIRCLE
> SIT (exact match)
Ask? L2+DOWN R1+CIRCLE L2+CIRCLE
> HERE (exact match)
Ask? R2+UP Triangle L2+CIRCLE
> FOR (exact match)
Ask? R2+RIGHT L2+LEFT
> THE (exact match)
Ask? R2+SQUARE L2+CIRCLE LEFT L1+SQUARE LEFT L2+SQUARE R2+CIRCLE
> PRESENT (approximate match)
Ask? R2+RIGHT R1+TRIANGLE L1+X
> THIS (exact match)
Ask? R1+TRIANGLE L1+X
> IS (exact match)
Ask? R2+TRIANGLE LEFT L2+TRIANGLE
> BELL (exact match)
Ask? UP
> I (exact match)
Ask? DOWN L2+X
> AM (approximate match)
Ask? R2+CIRCLE R1+CIRCLE X L2+CIRCLE START
> TIARA (exact match)
Ask? L2+SQUARE X R2+CIRCLE
> NOT (exact match)
Ask? R2+TRIANGLE LEFT L2+TRIANGLE
> BELL (exact match)
Ask? R2+SQUARE L2+CIRCLE LEFT L1+X
> PRESS (exact match)
Ask? L2+SQUARE R1+TRIANGLE R2+UP R2+CIRCLE R1+CIRCLE
> NIFTY (exact match)
Ask? L2+START L2+LEFT R2+CIRCLE
> WHAT (exact match)
Ask? L2+SQUARE R1+TRIANGLE R2+UP R2+CIRCLE R1+CIRCLE
> NIFTY (exact match)
Ask? L2+SQUARE R1+UP
> KNOW (exact match)
Ask? R2+SQUARE L2+TRIANGLE R1+X
> PLAY (exact match)
Ask? L2+START L2+LEFT L2+SQUARE
> ONE (exact match)
Ask? R2+SQUARE L2+CIRCLE LEFT L1+X
> PRESS (exact match)
Ask? L2+SQUARE R1+TRIANGLE R2+UP R2+CIRCLE R1+CIRCLE
> NIFTY (exact match)
One mistake here: the "NO" ends up as "KNOW" since they are homophones.
An unknown button is cut off-screen during the PLAYER line, which is why it is only PLAY here. I am inclined to believe that it is either CIRCLE or RIGHT since there was only one button press noise made, and whatever button it is makes the ER sound which is new -- it's not an existing one button sound. Personally I find RIGHT to be a more likely candidate than CIRCLE, but I will not add either to the table until we actually see it on-screen.
For the purposes of this, I've assumed DOWN is schwa (AX/ə), but this is still up for debate.
The dictionary is a BK-tree (https://en.wikipedia.org/wiki/BK-tree) mapping lists of the phonemes to words with that pronunciation. I used standard Levenshtein distance as the metric and the default threshold when searching is a distance of 1, meaning that you can add, delete, or change one of the phonemes and still have a match. This seems to work pretty well.
The frequency data is just a normal hash table matching stems (using Porter stemming) to their rank from the word frequency list I got. Both of these are constructed by make_dict.go
, which then serializes them in delightful Go .gob
files.
The main program petscop_translator.go
reads in a line of button combinations and parses them (query.go:ParseButtons
) into a list of 2-tuples like {"L2", "X"}, {"START"}...
. (I used pretty much the same syntax /u/LittlestPetscop did, including calling the cross X because it's faster to type.)
It then maps the buttons into possible ARPAbet phonemes using the mapping in arpabet.go
. I say possible because to account for the strange accent, a few of the buttons have multiple possible phonemes. When computing the Levenshtein distance, anything that equals one of the possibilities is a match (no substitution needed).
After that, it queries the BK-tree (bktree.go
) and gets back a list of matches and their Levenshtein distances away from the input key. I pick the best match (query.go:PickMatch
) by favoring lower Levenshtein distances, and tie breaking based on which word had a more frequent stem in the frequency data.
- Better frequency data. Using just individual words is kind of stupid, n-grams would be much better. (considers previous words)
- Part of speech tagging. This could be used to better pick a single match, e.g. it's unlikely that a verb will come after another in a sentence.
- Travis & AppVeyor, so you don't have to build it by hand...
- A webapp version using GopherJS.