Who's that pokemon (based on its etymology)? As we all know, most Pokemon are created by combining some English (or Japanese) words; these words are the Pokemon's "etymology." For example, "charmander" is "char" and "salamander" put together.

Your task is, given a Pokemon's etymology and number, output the original Pokemon name. A short answer will be able to compress the 890 Pokemon into much fewer characters by exploiting the fact that the etymologies and the names themselves use mostly the same characters.

Input: The Pokemon number, and an array/list of etymologies, all lowercase

Output: The Pokemon's name, also lowercase

The full list of test cases can be found here: https://pastebin.com/B3rRdiPn. Each line is a test case with the format:

&lt;pokemon number&gt;;&lt;comma separated etymology list&gt;;&lt;pokemon name&gt;


This list was created from https://pokemondb.net/etymology by running the following code:

let collectedRows = [];
let rows = $(".data-table tr").slice(1)
let isOddPrev = false;
// Replacements due to typos, remove 'palindrome' since girafig doesn't come from the word 'palindrome', 'palindrome' is just a descriptor
var repl = {"anacondo": "anaconda", "armadilo": "armadillo", "[palindrome]": ""}
rows.each((_, row) =&gt; {
  let isOdd = row.classList.contains("odd");
  let etym = $(row).find(".cell-etym-word").text().toLowerCase();
  if (repl.hasOwnProperty(etym)) etym = repl[etym];
  if (isOdd !== isOddPrev) {
    collectedRows.push({name: $(row).find(".cell-name").text().toLowerCase(), etymology: [etym], num: parseInt($(row).find(".cell-num").text().replace(/\s/g, ''), 10)});
  } else {
    collectedRows[collectedRows.length - 1].etymology.push(etym);
  }

  isOddPrev = isOdd;
});

// Output:
collectedRows.map(pokemon =&gt; pokemon.num + ";" + pokemon.etymology.join(",").replace(/\,$/, '') + ";" + pokemon.name).join("\n")


Examples:

The full list of test cases can be found at the pastebin link, but here are some example inputs and outputs:

1, ['bulb', '-saur'] --&gt; 'bulbasaur'
122, ['mr.', 'mime artist'] --&gt; 'mr. mime'
29, ['nidoru' ,'rhino', '♀'] --&gt; 'nidoran♀'
16, ['pigeon'] --&gt; 'pidgey'
17, ['pigeon'] --&gt; 'pidgeotto'
23, ['snake'] --&gt; 'ekans'


Note that due to 'nidoran♀' and its family, you need to be able to handle input/output in UTF-8.

The winner is the entry with the highest accuracy, with ties broken by code length. In practice, this means that the challenge is code-golf -- you need to get 100% accuracy to be a competitive entry -- since you can trivially create a lookup table, but feel free to post a sub-100% accuracy answer if you think it does something creative (someone else might extend it to handle the edge cases).

If you wish, you may also include a data file, and the number of bytes of that file is added to your byte count. This is just if you want to keep your code and data separate.
JavaScript (Node.js 11.7+), 100%, 2094 bytes
Same as below, with the data file compressed with Brotli instead of zlib.
n=&gt;a=&gt;(require('zlib').brotliDecompressSync(require('fs').readFileSync('a'))+'').split`/`[n-1].replace(/\d.\d?/g,([i,p,l])=&gt;a[i].substr(p,++l||9))

Try it online! (not working on TIO)

JavaScript (Node.js), 100%,  2176 2158  2151 bytes
This is similar to the self-contained version below but uses an external data file of 2010 bytes named 'a'. The TIO link generates a local copy of this file.
The final size was computed according to the rules for scoring multiple files:
$$2010+140+1=2151$$
n=&gt;a=&gt;(require('zlib').inflateRawSync(require('fs').readFileSync('a'))+'').split`/`[n-1].replace(/\d.\d?/g,([i,p,l])=&gt;a[i].substr(p,++l||9))

Try it online!

JavaScript (Node.js), 100%,  2701 ... 2359  2356 bytes
Takes input as (id)([etymology0, etymology1, ...]).
The source contains many unprintable characters.
n=&gt;g=(a,i=2,s="&#20;aZOEDQ]^4+24&gt;&lt;e_&lt;Z&#18;Z)aku&#7;[XdFyXd&#28;ttoXd&#28;t|63a%&#25;}5&#6;k&#5;s/arbok_[__S&#5;♀E2a_S&#5;♂E_]]Sx_Sy`SytuffS[K~_SB]NO[&#3;h~[]?4PS@NB;2&#21;&#21;ZW)H~~pBR_&#30;]3!AA[&#27;r~?6G/@W@GA;5'dOOXel?3U~rXk~d,/!Z%~/&#16;@82arXix_S)ZD_~?3=I?3=CO;ak*@*@&amp;tuV2)offH&#18;UXy`Xy`WseyWelaSs18/Ta*R[WyuWmie&#22;4~r/jyUS(~r[WosWkZXarados/lapras~&#6;eveeG_NXrT/om&#5;y&#19;/om(~~ps}5_XT3`[]*i&#30;&amp;te[~&#26;P2Z[2efD/cyF(4]Q%NB4r&gt;KW&#20;_/le&#28;&#17;ledi&#5;&#29;kXi([&#23;0Tu_$~f&#17;0,`Ye`Ye`/n&#20;/x`MXZ!ORR+22&#15;T3`o&#11;ed[]]/ai!/2HV2^3~J_[_]W@+v&#16;XP]SarigWH2K3P+ce*&amp;x&#24;&#24;/qwF`YzH2&gt;Y(QW&#31;_&#11;M5]N+&#14;KW@]$NN&#29;&#23;`2K4XrT42OBR*@~um[M[&#27;y[2u&#2;~cZeW&#31;Y&#31;S&#31;Xgi&#17;ho-ohWbiRWy,&gt;E2^4&#2;C;3NBN&#14;^4%e&#18;uFVM&lt;17M]2|6XV4N3&#9;SryD&#25;]Q/TtsJG+~8VA/U4,&#26;VE[ME]&#29;]2dEWH~]Q~[NOXVRRN&#24;&lt;CQ5~leWn[Jse~l&#8;JQYvIQ&#18;ail`3&#18;a&#31;K]&#26;Z$NQ]B/&#19;3T3YnZ&#25;2e]2[/z=IO&#9;]&#11;,/corphH_[&#29;M3]Y&#16;2I?4ldo]2/&#16;4I/P4`)ec,XZ}7RM&#25;NS&#18;y&#7;ut[KR]]2n/@eI]]{64/luv`XIQ{VC/P30TR__]_YiZYiZ)yogre_*z&#17;jir01Ss]K]_M&#30;O_KSy&#24;]XdZ2f|VCL6ZL6ZWx&amp;oMO&#12;NA&#14;A/buV&#23;Z]/H4Z{TnGXizC~zC?7{6{V&#30;W!Q&#24;[yO*iISkZ&#9;M?2Uy_&gt;N]Sjr.]]A/gZ@6$[E/=olu/luca=o~sE222&#15;kFuP2%_NA[N]MWa0b4[S&#16;&amp;&#20;yXyV]SivZ]_]3__+Yo`XrT-zBB__/r0Im/uV$5/az`Ylg&#17;palkia]3_Q&#29;i&#17;phV~phyRYyminO&#2;ni$[&gt;WgMO/oshaw!*{TW=S&#19;&lt;pDGQK###XnV2?5r222$~lYez,&gt;[5aXggen&#16;3aXT2`&#9;_&#29;_&#12;2W&#4;r/guF&#4;r&#26;`3&#4;rDA&#30;Wh&#15;awkMWlI]%&gt;DJeeS&#31;4|VZ2Y0UIK5&#12;)=ko=kL4C^3;ni&#8;BM&gt;~QEO|72ri18/tiFoPYF`WenWH3sXZ_YuaYoZYccV/c&#28;cV&gt;QSt18DME2,]~&#7;Jite^3^3]&#15;aws`&#6;0UlH)FT3ZR/fooUY&#28;&#19;s{6&#12;/alo0U0P&amp;k^5GGLLL4ZXV&#27;kC3k&#27;kC222&#6;lgyem/beheeyem[D&lt;Z~wYxHXxo24MQ&#25;Q&#25;2r&#29;k~foo&#24;XZ3iTQOQRJlH&#3;RK^IWH3]/d&#20;oQ?4F23]ENSkZWzZAS&#4;Z$4am~kVm~or&#21;mM}53aR}53B&#12;GRNK2ieD]Dby~sby^5AG&lt;`RD[O];M3sQR+++OR]EN&#27;e&lt;V&amp;xNMK4XI^4&#15;kFV&#29;eMerQE&gt;OBY082a?50aYv`[5X0TneRKYg`*LkiEMSa17&#12;&#23;&#8;&#9;M/xZ3eas/Z3lt25/zy`Yncie~aA/&#28;VR&#30;KW(&lt;Z~pC3}5&#25;Xk&#31;2k_^3/y&#28;C3^52?7Wj(Xk(]R8Yc0PGO321_{T&#2;&#19;4iO[[|5OO3H/fo`ONW23RR~ul/!01_/H5072RaWey&#14;]D]GYos`/py=17@Vu~: `_D?2FIQ~de`Wkyu]K/d`4T&#11;-o&#11;-oL4-o&#22;3o~ V4&#22;&#22;iSg{UYg(]G]&lt;V/x&#4;kH3ee&#14;aXrUNXcT2zC$&#7;[[_&#26;a=ka[7nYaZ&#9;Q&#27;y/U5`_O&#30;K{7{6&#15;kwZB{6GG_&#3;lerO&amp;t{VW&#31;&#24;W=/dF`Q;2VXIR&#11;F&#26;olQ&#2;nRG&#14;RDWk&#8;{P07[2%&gt;SskVB&#30;3&lt;aSa&#19;&#27;n&#7;&#3;rem&#3;Z&amp;&#8;@1]3~m`SgC/pZ2V]3%5'd~ riI~rig17K|Vmie/fF&#21;,Ym_R&#6;isP2eJT2%~f,A~zZWozZ~vZWovZAXV&#26;ZQ/zaciU/za0UzTJtus~/&#20;}W1|Y1{S1`10_}0^J1]}1[|0Z11Y~3X~2W~4V12U0!03S~5R}2Q}3P04O|1N{1M|2L)01K|3J~6I13H05G{0FU2E{2D}4C14B{3A^1@20?/`&gt;{4=0V&lt;~7;YP,15+[4*[3)/k(a`&amp;~i%|4$X`#/P3`_!2T&#31;i`&#30;J`&#29;_3&#28;U3&#27;~e&#26;Yk&#25;{5&#24;_4&#23;/T4&#22;~ `&#21;Z;2&#20;00&#19;06&#18;/w&#17;a/&#16;09&#15;/s&#14;^2&#12;}6&#11;_2&#9;][&#8;16&#7;na&#6;/e&#5;an&#4;ur&#3;~t&#2;Wi")=&gt;i&lt;127?g(a,i+1,/[\r\n "'\--:\\a-z]/.test(c=Buffer([i]))?s:(l=s.split(c)).join(l.pop())):s.split`/`[n-1].replace(/\d.\d?/g,([i,p,l])=&gt;a[i].substr('0x'+p,l||9))

Try it online!
How?
Each name is encoded as a sequence of sub-strings from the etymology words, with some possible hard-coded characters in between.
Example:
For the first name, we are given:
n = 1, a = ['bulb', '-saur']

The name bulbasaur is first encoded as 004a114, which is interpreted as:
'004' -&gt; a[0].substr(0, 4) -&gt; 'bulb'
'a'   -&gt; hard-coded character 'a'
'114' -&gt; a[1].substr(1, 4) -&gt; 'saur'

But the length of the sub-string is omitted whenever this is possible without resulting in any ambiguity. When the length is omitted, the decoder assumes that the word must be read to its end.
Both lengths can be omitted in this case, so the final encoding is:
00a11

All the 890 encoding sequences are concatenated together, using / as a separator. This results in a string with a lot of repeated patterns which can be efficiently crushed:
00a11/00211/00412/00314/00313/00311/00514/00210324/00414/006e/00310/.../za001z03/005tus