Create a World Cup guessing program

As you know, the World Cup group stage is over, and from tomorrow the best 16 teams will commence the knockout stage:

• Brazil (BRA)
• Mexico (MEX)
• Netherlands (NED)
• Chile (CHI)
• Colombia (COL)
• Greece (GRE)
• Costa Rica (CRC)
• Uruguay (URU)
• France (FRA)
• Switzerland (SUI)
• Argentina (ARG)
• Nigeria (NGA)
• Germany (GER)
• United States (USA)
• Belgium (BEL)
• Algeria (ALG)

In the knockout stage, after each match the winner gets through to the next round, and the loser goes home (there are no draws). Click here to see more about the knockout stage.

You have been hired by golfbet.com, a new betting website because you are known to be good at both programming and sports betting. Your task is to write a program or function which can guess the winner of a match. Of course, everybody makes different guesses, it doesn't matter as long as your guesses are consistent.

If you don't want to guess, you can use the following guesses:

BRA
BRA
CHI
BRA
COL
COL
URU
GER
FRA
FRA
NGA
GER
GER
GER
ALG
GER
NED
NED
MEX
NED
CRC
CRC
GRE
ARG
ARG
ARG
SUI
ARG
BEL
BEL
USA

1. The program has to output the same winner regardless the order of the to teams (the winner of the BRA-CHI match has to be the same as of the CHI-BRA match)
2. If a team loses, it cannot play any more matches. This means for matches that don't take place, you have to indicate so. For example if your program guesses Brazil to win the BRA-CHI match, then CHI-GER has to return "no result", because Chile won't play against Germany. See the link above for schedule.

For the sake of simplicity you don't have to deal with the bronze match (but you can of course).

Your program or function takes two strings as input: the 3-letter country code of the two teams and returns the country code of the winning team (you can use standard input/output, or two function parameters/return value). If the given two teams will not play according to your guesses, you must return something else (this can be anything but the country codes, eg empty string, null, error message). You can assume the input is correct (two different country codes which are in the list).

This is primarily a code-golf, so shortest program in bytes wins. However, nice and tricky solutions are also valuable.

Example (of course, you can make your own guesses):

input: BRA CHI output: BRA

input: CHI BRA output: BRA

input: CHI GER output: no result

• Should've been an [underhanded] that outputs random guesses, but always let's your favorite team win ;) (As in: outputs a random guess-tree) – ɐɔıʇǝɥʇuʎs Jun 27 '14 at 17:04
• @ɐɔıʇǝɥʇuʎs codegolf.stackexchange.com/questions/32092/… This one is very similar to that – David Frank Jun 27 '14 at 17:06
• @ɐɔıʇǝɥʇuʎs, this is far more interesting as is. We're given a space of possible programs, roughly corresponding to hash functions onto a three-element set, and we have to find the smallest program which is equivalent to any program in that space. – Peter Taylor Jun 27 '14 at 17:11
• Can be assume the input will be valid or would BRA BRA and HAZ CHEEZBURGER have to be handled properly? – Dennis Jun 27 '14 at 20:23
• @Dennis see my edit — the input is always valid – David Frank Jun 27 '14 at 20:28

Python 2.x - 368 283

Interesting challenge. Of course we need to get present rankings from the FIFA. Brazil have the so-called "12th man" as they have home advantage therefore the weighting 12/11.

a='BRA CHI COL URU FRA NGA ALG GER MEX NED CRC GRE ARG SUI BEL USA'.split()
d=[1242*12/11,1026,1137,1147,913,640,858,1300,882,981,762,1064,1175,1149,1074,1035]
m={}
for n in[16,8,4,2]:
j=0
for k in range(0,n,2):
s=a[k] if d[k]>d[k+1] else a[k+1]
m[a[k]+' '+a[k+1]]=s
a[j]=s
j+=1
def f(s):
try: print m[s]
except: print 'no result'


Tips to shorten the above are welcome :-).

Improvements thanks to @TheRare and @MrLemon

a='BRA CHI COL URU FRA NGA ALG GER MEX NED CRC GRE ARG SUI BEL USA'.split()
d=15,6,10,11,4,0,2,14,3,5,1,8,13,12,9,7
m={}
for n in 16,8,4,2:
j=0
for k in range(0,n,2):s=a[k]if d[k]>d[k+1]else a[k+1];m[a[k]+' '+a[k+1]]=s;a[j]=s;j+=1
def f(s):print s in m and m[s]or'no result'


This leads to the following results:

BRA CHI: BRA
COL URU: URU
FRA NGA: FRA
ALG GER: GER
MEX NED: NED
CRC GRE: GRE
ARG SUI: ARG
BEL USA: BEL
------------
BRA URU: BRA
FRA GER: GER
NED GRE: GRE
ARG BEL: ARG
------------
BRA GER: BRA
GRE ARG: ARG
------------
BRA ARG: BRA


Example calls:

f('BRA MEX')
no result
f('BRA CHI')
BRA

• 1. You don't use j (it's always zero). 2. for k can be written in one line by using ; delimiter. 3. You don't need space after keywords like try or except 4. You can remove spaces between brackets and keywords (a[k]if d[k]>d[k+1]else a[k+1] is valid) 5. for n in 16,8,4,2 6. def f(s):print s in m and m[s]or'no result' – seequ Jun 30 '14 at 9:58
• @TheRare He does use j, but there's a slight formatting error with the j+=1. And you can save lots of characters by reencoding the FIFA data to [15,6,10,11,4,0,2,14,3,5,1,8,13,12,9,7], of course. – MrLemon Jun 30 '14 at 13:14
• @MrLemon Ah, I didn't try the code myself, just looked at it. – seequ Jun 30 '14 at 13:35

C, 182 178 133 (or 126)

Not the shortest program here, but it is the shortest in which the prediction can be changed easily. Now that all the semifinalists are known, I'm updating.

There's some changes to the code, too. Apart from Dennis's suggestions in the comments, the program has been converted to a function (as on re-reading this is allowed by the rules) and the hashing has been shortened.

Code,133

f(char*a,char*b){
char*p,*t=" HIAEAIH N?=R=?N ;@4S4@; 5BDGDB5 B@?I?@B",h[3]={*a-a[1]%16,*b-b[1]%16};
(p=strstr(t,h))&&puts(p-t&2?a:b);
}


How it works

the inputs a and b are hashed by the expression *a-a[1]%16 to a single character (*a is a shorter equivalent to a[0]). The hash results for teams a and b are stored in h. For example BRA CHI becomes @;. Hashed values are as follows (the confirmed semifinalists and my predicted champion are marked with *.)

GRE E   CRC A   *NED I   MEX H
USA R   BEL =   *ARG ?   SUI N
URU S   COL 4  **BRA @   CHI ;
NGA G   FRA D   *GER B   ALG 5


t[] stores my predictions. The results of the round of 16 and quarter finals are now known. Each group of 4 teams is ordered such that the 1st and 4th were eliminated, and the 3rd is the semifinalist. Similarly of the semifinalists, I'm predicting that the 1st and 4th will be eliminated and the 3rd semifinalist will be the overall winner. If you disagree with my predictions, simply reorder the table.

The predictions are stored in palindromic blocks to accommodate the possibility of the user entering the teams in either possible order. The ordering puts the winning teams of each set of 4 together to play a third match. Thus in the first group GREE lost to CRC and MEXH lost to NED. This sets up CRCA to play NEDI in the quarter final without having to repeat the typing. The string is padded with a space between each group of 4 teams / 7 characters to help ensure there is no output for teams that will not play each other.

The winner of each possible match in each group of 8 characters is as follows: invalid,b,a,a,b,b,a,invalid. Thus the correct choice of winner can be made by taking the position of h in t AND 2. Unfortunately the strstr function is not the most straighforward as it returns a pointer p, so we must subtract p from t to get the actual position in t. If the match is invalid (cannot be found in t), p is zero and the phrase no result is printed.

Some dubious improvements, 126

2 characters saved by an improved hash expression. Unfortunately, this requires the case of the teams to be as shown in the test program below the function (eg Bra instead of BRA as used in the program above.) I've satisfied myself that there is no way of doing this with a single operator, so 2 operators and a single-character constant is as good as it gets. Note also that Uru maps to space so an alternate character | is needed to separate the groups of teamcodes.

5 characters saved by eliminating t and treating the prediction string as a literal. This means it is impossible to know the address where the string is stored. However, provided it is not stored at zero, we are only interested in p&2 so the code will work if the address is divisible by 4. (Note that it is not allowed to treat pointer p directly as an integer, it must be subtracted from another pointer. I use the pointer a so a must also be divisible by 4.) One can be fairly confident on a 32 or 64 bit compiler/architecture strings will be stored in this way. This has been working fine for me on GCC/cygwin, though it refuses to compile on visual studio / windows.

g(char*a,char*b){
char*p,h[3]={*a^a[1]+3,*b^b[1]+3};
(p=strstr("|%&626&%|+4*#*4+|(71 17(|./3$3/.|/74&47/",h))&&puts(p-a&2?a:b); } main(){ char team[16][4]={"Gre","Crc","Ned","Mex", "Usa","Bel","Arg","Sui", "Uru","Col","Bra","Chi", "Nga","Fra","Ger","Alg"}; int i; for(i=1;i<16;i++){printf("%s %s \n",team[i-1],team[i]);g(team[i],team[i-1]);g(team[i-1],team[i]);} }  • Interesting approach! You can save a few bytes by removing the space between char and *p and replacing a[0], b[0] and h[0] with *a, b and *h. Also, the question says that *if the given two teams will not play according to your guesses, you must return something else (this can be anything but the country codes, eg empty string, null, error message), so printing no result is not required and you could replace puts(...) with (p=strstr(t,h))&&puts(p-t&2?a:b). – Dennis Jun 30 '14 at 16:21 • Thanks for the tips, especially a[0]->*a! Will update soon. Just wondering, is there any way to access a multidimensional array with a single number? I wrote this command line version of identical length (can save 1 byte with a #define.) It would be nice to avoid the double subscripts in this and similar cases: char *p,h[2],*t="-LgRrRgL bA9j9Ab hp535ph OKYtYKO KpAgApK";main(int c,char**v){h[0]=v[1][1]*3-v[1][0]*2;h[1]=v[2][1]*3-v[2][0]*2;puts((p=strstr(t,h))?v[1+!(p-t&2)]:"no result");} – Level River St Jun 30 '14 at 21:36 • 1. I found a small bug. h should be null-terminated, so it has to be h[3]. 2. If you set p=v[1], you can access v[i][j] as p[4*(i-1)+j]. It works on my machine, but I don't know if it's portable... 3. You should be able to initialize h if you declare it inside main: main(int c,char**v){char*t="-LgRrRgL bA9j9Ab hp535ph OKYtYKO KpAgApK",*p=v[1],h[3]={p[1]*3-*p*2,p[5]*3-p[4]*2};(p=strstr(t,h))&&puts(v[1+!(p-t&2)]);} – Dennis Jun 30 '14 at 23:50 • @Dennis your code works fine on both GCC/cygwin and VS/Windows on my machine. Also for(int i=0;i<1000;i++)printf("%d %c ",i,i[*argv]) echoes back evey printable character of the command line on VS, but on GCC the program name is at 0, the first argument is at 40 and the second argument is nowhere to be seen (i went up to 1000.) Curious. Anyway, I changed to a function which is within the rules, in addition to updating my predictions (I was just waiting for tonight's result to confirm them before posting.) Thanks again and good luck against Brazil on Tuesday. – Level River St Jul 6 '14 at 0:06 JavaScript 215 206 120 116 A lot of room for improvement: ES5 - 215 a=prompt().split(' ');("BRACHI0COLURU0FRANGA0GERALG0NEDMEX0CRCGRE0ARGSUI0BELUSA0BRACOL0FRAGER0NEDCRC0ARGBEL0BRAGER0NEDARG0BRANED".split(0).filter(function(x){return x.match(a[0])&&x.match(a[1])})[0]||"").substr(0,3)  ES6 - 206 a=prompt().split(' ');("BRACHI0COLURU0FRANGA0GERALG0NEDMEX0CRCGRE0ARGSUI0BELUSA0BRACOL0FRAGER0NEDCRC0ARGBEL0BRAGER0NEDARG0BRANED".split(0).filter((x)=>{return x.match(a[0])&&x.match(a[1])})[0]||"").substr(0,3)  Regex Approach - 116 Thanks to ɐɔıʇǝɥʇuʎs for posting this link, it helped me making the regex a=prompt().split(' ').sort();a.join('').match(/LG(.R(A|G)|GE)|RG(S|BE|CR)|ELUS|AC(H|O)|OLUR|CGR|CM|FRANG|XNE/)&&a[0]  • You can shorten your solutions by converting split(' ') to split and join('') to join. – Arjun Jun 1 '16 at 2:40 Python (179148 139 c.q. way too long) f=lambda *l:sorted(l)[0]if"".join(sorted(l))in"BRACHI COLURU FRANGA ALGGER MEXNED CRCGRE ARGSUI BELUSA BRACOL ALGFRA CRCMEX ARGBEL BRAFRA CRCMEX ARGBEL BRAFRA ARGCRC ARGBRA"else 0  Everybody knows the country with the name that comes first in the alphabet is going to win. (This answer just exists to get things started) Thanks to the charity of the guy(s) over here, I could shorten my answer by a bit: import re;f=lambda *l:sorted(l)[0]if re.match('RGB|CM|RGS|CGR|L.F|XNE|EL.S|O.UR|RGCR|B.AF|L.GE|^BRACHI$|^FRANGA$|^BRACOL$',"".join(sorted(l)))else 0


This does assume valid teams, but doesn't need a valid line-up (f('BRA','NED') would return 0 (invalid match), but f('XNE') would return 'XNE'. I don't get from your question that this is a problem though. Feel free to re-abuse this regex as you see fit.

Thanks @Ventero, I know nothing about regexes.

import re;f=lambda*l:sorted(l)[0]if re.match('RGB|CM|RGS|CGR|L.F|XNE|EL.S|O.UR|RGCR|B.AF|L.GE|BRACHI|FRANGA|BRACOL',"".join(sorted(l)))else 0

• You probably want re.search in the golfed version, not re.match. Also, you should be able to drop the ^ and $ anchors. – Ventero Jun 27 '14 at 20:54 Scala (150) type s=String;var m=Map[s,s]();def f(x:(s,s))={var a=x._1;var b=x._2;if(b<a){b=a;a=x._1};if(m.getOrElse(a,a)=="")m.getOrElse(b,b)else{m=m+(b->"");a}}  Here are matches between "foo" and "bar" possible, also teams wich won't play in real against each other in the first rounds will have a result.(for example starting with BRA,ARG) It's just recording loosing teams. type s=String //just aliasing for saving characters var m=Map[s,s]() //map for storing loosing teams, a set would do too def f(x:(s,s))={ var a=x._1 var b=x._2 if(b<a){b=a;a=x._1};//swap if b<a lexographically if(m.getOrElse(a,a)=="")//if a has loosed previously m.getOrElse(b,b)// return b if b was not in the map else return "" else{ m=m+(b->"") //add b to the map, because a will definitly win this amazing match a //and return a } }  Called with: f(("GER","BRA"))  PowerShell (261 221) $a=Read-Host;$b=Read-Host;$x="CHIBRA","URUCOL","FRANGA","ALGGER","MEXNED","CRCGRE","SUIARG","BELUSA";0..($x.Length-2)|%{$x+=$x[2*$_].Substring(3)+$x[2*$_+1].Substring(3)};$x|?{"$a$b","$b$a"-eq$_}|%{$_.Substring(3);exit};0  As a relatively new PowerShell user, I find the pipeline absolutely amazing. I think next up might be to try and fiddle with the array to hopefully eliminate all these substring calls. (I had to add a call at the end or else it outputted both teams) New member, first attempt at code golf! Could have hard-coded the quarterfinal, semis, and finals to save a few characters, but >that wouldn't be quite as fun. Should be simple enough to decipher, but it fulfills both conditions: gives same winner >regardless of order entered, and only returns a winner for matches that actually take >place! Any advice for improvement would be greatly appreciated, thanks! Original $a=Read-Host;$b=Read-Host;$x=("CHIBRA","URUCOL","FRANGA","ALGGER","MEXNED","CRCGRE","SUIARG","BELUSA");for($c=0;$c-lt$x.length-1;$c+=2){$x+=$x[$c].Substring(3)+$x[$c+1].Substring(3)}foreach($i in $x){if($i-match$a-and$i-match$b){return$i.Substring(3)}}return 0

• A few things (fixing this completely would take a lot more time): Don't write PowerShell as if it were C or C#. This means, return is unnecessary in most cases. foreach and most explicit loops are overrated and useless. Methods like .Substring should be only used in extreme circumstances (and while the utility of football might be debated, it's not an extreme sport). Generally you want to use the pipeline as much as possible. – Joey Jun 28 '14 at 10:02
• Instead of writing a long-winded for loop like for($c=0;$c-lt$x.length-1;$c+=2){$x+=$x[$c].Substring(3)+$x[$c+1].Substring(3)} you can just use a pipeline with a range and ForEach-Object (aliased %): 0..($x.Length/2)|%{$x+=$x[2*$_].Substring(3)+$x[2*$_+1].Substring(3)}. By carefully considering how your data is represented you can very likely get rid of the Substring calls. The parentheses around the initial array are unneeded. For larger arrays it also can make sense to use a separator character and use -split (unary -split in case of the separator being space or tab). – Joey Jun 28 '14 at 10:05 • The final foreach loop for example can be written as a pipeline as well (? is Where-Object): $x|?{$_-match$a-and$_-match$b}|%{$_;exit};0 which essentially says »Filter every element in $x for whether it matches both $a and $b and output the first one, exiting afterwards. If none could be found, output 0.«. Since you know the format of your strings you can also just use -match"$a$b|$b$a", I guess. Because they just have to appear in either order in the string. Which also means that we can use a little trick here: "$a$b","$b$a"-eq$_ to golf yet another byte. – Joey Jun 28 '14 at 10:12 CJam, 64 58 bytes lS/$_0=\:+4b256b1>:ca"oM-YtM-mM-^@}gM-^VM-^U8tM-=nM-^MfM-]oM-xgM-)tM-|m@gim{g_"2/&,*


The above uses caret and M- notation, since the code contains unprintable characters.

At the cost of six additional bytes, those characters can be avoided:

lS/$_0=\:+4b95b1>32f+:c"I8Vyv)2~N{VIEh1$IW32W)B82QBs2G"2/N*\#W>*


Try it online.

Test run

$base64 -d > worldcup.cjam <<< \ > bFMvJF8wPVw6KzRiMjU2YjE+OmNhIm/ZdO2AfWeWlTh0vW6NZt1v+GepdPxtQGdpbXtnXyIyLyYsKg==$ wc -c worldcup.cjam
58 worldcup.cjam
$for A in ALG ARG BEL BRA CHI COL CRC FRA GER GRE MEX NED NGA SUI URU USA; do > for B in ALG ARG BEL BRA CHI COL CRC FRA GER GRE MEX NED NGA SUI URU USA; do > [[$A < $B ]] && echo$A - $B :$(LANG=en_US cjam worldcup.cjam <<< "$A$B")
> done; done | grep ': .'
ALG - ARG : ALG
ALG - BRA : ALG
ALG - FRA : ALG
ALG - GER : ALG
ARG - BEL : ARG
ARG - CRC : ARG
ARG - SUI : ARG
BEL - USA : BEL
BRA - CHI : BRA
BRA - COL : BRA
COL - URU : COL
CRC - GRE : CRC
CRC - MEX : CRC
FRA - NGA : FRA
MEX - NED : MEX


lS/$" Read one line from STDIN, split at spaces and sort the resulting array. "; _0=\ " Extract the first element of a copy of the array and swap it with the array. "; :+ " Concatenate the strings. "; 4b " Convert the resulting string into an integer by considering it a base 4 number. "; 256b " Convert the integer into an array by considering it a base 256 number. "; 1>:ca " Drop the first element, convert into a string and create a singleton array. "; "…" " Push a string of all matches encoded as explained above. "; 2/ " Split the string into an array of two-character strings. "; & " Intersect the two arrays. If the array is non-empty, the teams play. "; ,* " Multiply the string on the stack by the length of the array. ";  • You should add the size of your bash script to the size of the CJam program to be fair. – David Frank Jun 28 '14 at 10:04 • @DavidFrank: I've included the Bash script to show proof that my program provides output only for the 15 matches that take place. It works on its own and requires no external programs. – Dennis Jun 28 '14 at 11:30 CJam, 49 48 bytes lS/$_0="^\16@&^^/+(^]^W^Y,>O?"{_2%+}3*@{2b91%c}%#1&!*


The above uses caret notation, since the code contains unprintable characters.

At the cost of two additional bytes, those characters can be avoided:

lS/$_0="(=BL2*;74)#%8J[K"{_2%+}3*@{2b91%C+c}%#1&!*  Try it online. Test run $ base64 -d > wc.cjam <<< bFMvJF8wPSIcMTZAJh4vKygdFxksPk8/IntfMiUrfTMqQHsyYjkxJWN9JSMxJiEq
$wc -c wc.cjam 48 wc.cjam$ for A in ALG ARG BEL BRA CHI COL CRC FRA GER GRE MEX NED NGA SUI URU USA; do
> for B in ALG ARG BEL BRA CHI COL CRC FRA GER GRE MEX NED NGA SUI URU USA; do
> [[ $A <$B ]] && echo $A -$B : $(cjam wc.cjam <<< "$A $B"); done; done | grep ': .' ALG - ARG : ALG ALG - BRA : ALG ALG - FRA : ALG ALG - GER : ALG ARG - BEL : ARG ARG - CRC : ARG ARG - SUI : ARG BEL - USA : BEL BRA - CHI : BRA BRA - COL : BRA COL - URU : COL CRC - GRE : CRC CRC - MEX : CRC FRA - NGA : FRA MEX - NED : MEX  Background We start by assigning and ASCII character to each team by considering its name a base 2 number, taking the resulting integer modulo 91, adding 12 (to avoid unprintable characters) and selecting the character corresponding to the resulting ASCII code. In CJam code, this is achieved by 2b91%c. For example, the character codes of ALG are 65 76 71. Since (4 × 65 + 2 × 76 + 71) = 483, 483 % 91 + 12 = 40 and 40 if the character code of (. This gives the following mapping: ALG ( ARG 4 BEL # BRA 2 CHI * COL ; CRC 8 FRA B GER = GRE J MEX [ NED K NGA L SUI ) URU 7 USA %  Now, we can encode the matches of the round of 16 as follows: (=BL2*;74)#%8J[K  If we assume that the first team in alphabetical order always wins, the matches of the quarter-finals are as follows: (B2;4#8[  Note that this string can be obtained from the first by selecting every second character, starting with the first. In CJam code, this is achieved by 2%. Using the same idea, the matches of the semi-finals and the final match are as follows: (248 (4  The code "(=BL2*;74)#%8J[K"{_2%+}3*  pushes the string containing the matches of the round of 16, then does the following thrice: duplicate the string, extract every second character of the copy, concatenate. The result is the string (=BL2*;74)#%8J[K(B2;4#8[(B2;4#8[(248(B2;4#8[(248(248(4  which contains all matches (some of them more than once). How it works lS/$        " Read one line from STDIN, split at spaces and sort the resulting array.     ";
_0=         " Extract the first element of a copy of the array.                           ";
"…"         " Push the string containing the matches of the round of 16.                  ";
{_2%+}3*    " Push the remaining matches.                                                 ";
@           " Rotate the input array on top of the stack.                                 ";
{2b91%C+c}% " Perform the mapping for each team in the input array.                       ";
#           " Push the index of the match in the array of all matches (-1 for not found). ";
1&!         " Push 1 if the index is even (valid match) and 0 if it is odd.               ";
,*          " Repeat the string on the stack that many times.                             ";

• It's amazing what you do with Cjam. Whenever I think you might borrow one of my ideas, you come up with something better! your prediction code "(=BL2*;74)#%8J[K"{_2%+}3* is the same length as the nonpalindromic form of my prediction string "rRgL j9Ab 35ph tYKO gApK" but a lot easier to handle. – Level River St Jun 30 '14 at 21:45

JavaScript 271

t=prompt('Match?').split('-')
x=t[0],y=t[1],T='BRACHICOLURUFRANGAGERALGNEDMEXCRCGREARGSUIBELUSA'
v='\n',R='',W='No Game'
for(z=1;T!='USA';++z,T=n){R+=v+z+v,n='',r=/(...)(...)/g
while(m=r.exec(T))a=m[1],n+=b=m[2],R+=a+'-'+b+v,W=a==x&&b==y||a==y&&b==x?b:W
}
`