# Code Golf Bingo!

You are given a Bingo board and a list of calls. You must print BINGO! as soon as your board wins the game.

Bingo boards look like this:

They will be specified like this:

14 29 38 52 74
4 18 33 46 62
7 16 * 60 71
9 27 44 51 67
12 23 35 47 73


Immediately following the board will be calls, like this:

B7
I29
G60
G51
O71
I23
I16
N38


You must echo the calls out to standard output until just after the call that makes you win (get a row, column, or 5-long diagonal all filled), then print BINGO!.

For the above example, print:

B7
I29
G60
G51
O71
I23
I16
BINGO!


Rules

Standard code-golf rules, shortest code wins.

Details

There will always be enough calls to guarantee you a Bingo. There will be no duplicate numbers on the board, and no duplicate calls. Boards will always have correctly matched numbers and letters (the B column contains only 1-15, the I column contains only 16-30, and so on), as will calls. The one and only free space will always be in the middle, marked with * instead of a number. Consuming and discarding calls from standard input after the winning call is allowed, but not required.

## Perl, 122 120 char

$b=join'. . ',map~~<>,0..4;while(<>){/(\d+)/;$b=~s/\b$1\b/*/;print;$b=~/(\*\s(\S+\s){$_}){4}\*/&&die"BINGO! "for 0..7}  Build the card in $b with two additional junky columns. Replace the numbers that are called on the card with * and print the called number. Then the last regular expression will evaluate to true when there are 5 regularly spaced *s on the board.

## C# – 536

(OK, this is probably not the most suitable language for that, but anyway…)

using System;using System.Collections.Generic;using System.Linq;class C{static void Main(){var s=Enumerable.Range(1,12).Select(_=>new HashSet<string>()).ToList();var b=Enumerable.Range(1,5).Select(_=>Console.ReadLine().Split(' ')).ToList();int i;for(i=0;i<5;++i){for(int j=0;j<5;++j){s[i].Add(b[i][j]);s[i+5].Add(b[j][i]);}s[10].Add(b[i][i]);s[11].Add(b[4-i][i]);}while(i>0){var l=Console.ReadLine();Console.WriteLine(l);l=l.Substring(1);foreach(var x in s){x.Remove("*");x.Remove(l);if(x.Count==0){Console.WriteLine("BINGO!");i=0;}}}}}


Formatted and commented:

using System;
using System.Collections.Generic;
using System.Linq;

class C
{
static void Main()
{
// all possible winnable five-item sets – any one of them need to be emptied to win
var s = Enumerable.Range(1, 12).Select(_ => new HashSet<string>()).ToList();
// read the board from input to a list of arrays of numbers
var b = Enumerable.Range(1, 5).Select(_ => Console.ReadLine().Split(' ')).ToList();
int i;
// split the board into the winnable sets
for (i = 0; i < 5; ++i)
{
for (int j = 0; j < 5; ++j)
{
// sets 0–4 represent rows
// sets 5–9 represent columns
}
// set 10 represent one diagonal
// set 11 represent the other diagonal
}
while (i > 0)
{
// read and echo another input
Console.WriteLine(l);
// ignore the initial letter – we are guaranteed it is correct, anyway
l = l.Substring(1);
// remove the number from all sets
foreach (var x in s)
{
x.Remove(l);
// also remove the center * (inside the loop just to shave off a few characters)
x.Remove("*");
// if any set became empty, we won!
if (x.Count == 0)
{
Console.WriteLine("BINGO!");
// ensure the loop will stop (might not be necessary per the rules, but anyway)
i = 0;
}
}
}
}
}


## Ruby 1.9 (194, 130)

This probably isn't the most sensible way to check for empty columns, but it was the first thing I thought to try! In particular, that #transpose costs a lot.

Either a blank line between the board and the numbers or fixed width fields when declaring the board would save a lot of characters. I couldn't think of a really nice way to read exactly 5 lines.

b=(R=0..4).map{gets}.join.scan /\d+|\*/
loop{gets
puts$_ ~/\d+/ (e=b.index$&)&&b[e]=?*
R.map{|y|$><<:BINGO!&&exit if R.map{|x|[b[5*x+y],b[5*y+x],b[y<1?x*6:4*x+4]]}.transpose.any?{|a|a==[?*]*5}}}  EDIT: 130 character solution using the regular expression technique from mob's perl answer: b=(0..4).map{gets}*'~ ~ ' loop{gets puts$_
~/\d+/
b[/\b#$&\b/]=?* 7.times{|i|$><<:BINGO!&&exit if b=~/(\*\s(\S+\s){#{i}}){4}\*/m}}


Given the long, long, long overdue announcement of Rebol's impending release as open source software, I returned to my pet dialect to solve this Bingo problem. I may soon be able to distribute Rebmu as its own, teensy GPL package. :)

## Rebmu 88 characters

In the compact notation:

rtZ5[GisGpcRaZisGaAPgPCaSB6zAPv'*]l5[AgL5[apGfAsk+A5]]hd+Gu[raGin-NTrM'*fisGv5]p"BINGO!"


The dialect uses a trick I call mushing which is explained on the Rebmu page. It's "legit" in the sense that it doesn't cheat the parser; this is valid Rebol...and can actually can be freely intermingled with ordinary code as well as (ahem) "long-form" Rebmu...which BTW would be 141 characters:

[rt z 5 [g: is g pc r a z is g a ap g pc a sb 6 z ap v '*] l 5 [a: g l 5 [ap g f a sk+ a 5]] hd+ g u [ra g in- nt r m '* fis g v 5] p "BINGO!"]

(Given that I claim the compression is a trick that one can do without the help of automation or compilation, I actually develop the code in the mushed form. It's not difficult.)

It's actually quite simple, nothing special--I'm sure other Rebol programmers could shave things off. Some commented source is on GitHub, but the main trick I use is to build all the possible solutions in a long series ("list", "array", what-have-you). I build the diagonal solutions during the input loop, as it takes five insertions at the head and five appends at the tail to make them...and there's already a five-iteration loop in progress.

The whole thing easily maps to Rebol code, and I haven't yet thrown any "matrix libraries" into Rebmu with transposition or other gimmicks that seem to come up often. Someday I will do that but for now I'm just trying to work relatively close to the medium of Rebol itself. Cryptic-looking things like:

 [g: is g pc r a z is g a ap g pc a sb 6 z ap v '*]


...are rather simple:

 [
; assign the series pointer "g" to the result of inserting
; the z'th element picked out of reading in some series
; from input that was stored in "a"...this pokes an element
; for the forward diagonal near the front of g
g: insert g (pick (readin-mu a) z)

; insert the read-in series "a" from above into "g" as well,
; but *after* the forward diagonal elements we've added...
insert g a

; for the reverse diagonal, subtract z from 6 and pick that
; (one-based) element out of the input that was stored in "a"
; so an element for the reverse diagonal is at the tail
append g (pick a (subtract 6 z))

; so long as we are counting to 5 anyway, go ahead and add an
; asterisk to a series we will use called "v" to search for
; a fulfilled solution later
append v '*
]


Note: Parentheses added above for clarity. But Rebol programmers (like English speakers) generally eschew the application of extra structural callouts for indicating the grammar in communication...rather save them for other applications...

Just as an added bonus to show how interesting this actually is, I'll throw in some mix of normal code to sum the board. The programming styles are actually...compatible:

rtZ5[GisGpcRaZisGaAPgPCaSB6zAPv'*]
temp-series: g
sum: 0
loop 5 * 5 [
square: first temp-series
if integer! == type? square [
sum: sum + square
]
temp-series: next temp-series
]
print ["Hey grandma, the board sum is" sum]


That's valid Rebmu as well, and it will give you a nice board sum before playing Bingo with you. In the example given, it says Hey grandma, the board sum is 912. Which is probably right. But you get the point. :)

# Mathematica 250

Disclosure: I assumed that the input was given in lists which are far more natural to use for Mathematica. So,with b representing the board and c representing the calls,

b//Grid
c//Column


If the input were to be in strings, the code would grow by about 30 characters. (I'll later include that variation.)

Code

y = ReplacePart[ConstantArray[0, {5, 5}], {3, 3} -> 1]; d = Diagonal;
t := Tr[BitAnd @@@ Join[y, Transpose@y, {d@y}, {d[Reverse /@ y]}]] > 0;
r@i_ :=(y = ReplacePart[y, Position[x, ToExpression@StringDrop[i, 1]][[1]] -> 1];
Print@If[t, Column[{i, "BINGO!"}], i])
n = 1; While[! t, r@c[[n]]; n++]


B7

I29

G60

G51

O71

I23

I16

BINGO!

# Python 249

R=raw_input;F=B=[[[x,0][x=='*']for x in row]for row in[R().split()for i in'11111']];A=any
while all([all(map(A,B)),all(map(A,zip(*B))),A(F[::6]),A(F[4:24:4])]):c=R();print c;C=c[1:];B=[[[x,0][x==C]for x in row]for row in B];F=sum(B,[])
print'BINGO!'


$./bingo.py < bingo.txt B7 I29 G60 G51 O71 I23 I16 BINGO!  • You could replace row by a one-char name. Untested: try i in'*'*5] and replace [x=='*'] with [x==i]. Nov 11 '13 at 22:38 ## APL (82) {(D≡(⍵∨⌽⍵)∧D←=/¨⍳⍴⍵)∨∨/(∧⌿⍵)∨∧/⍵:'BINGO!'⋄∇⍵∨B=⍎1↓⎕←⍞}0=B←↑{⍎(K,K)[⍞⍳⍨K←11↑⎕D]}¨⍳5  • {...}¨⍳5: do 5 times: • ⍎(K,K)[⍞⍳⍨K←11↑⎕D]: read a line (⍞) and map all characters that aren't digits or space to 0, then evaluate the line. • B←↑: turn it into a matrix (5x5 if the input was correct), and store in B. • {...}0=B: the starting board has a 1 in the free space (0) and 0 in the other spaces. • (D≡(⍵∨⌽⍵)∧D←=/¨⍳⍴⍵)∨∨/(∧⌿⍵)∨∧/⍵: if a line, a column, or a diagonal is filled: • 'BINGO!': then output BINGO • ∇⍵∨B=⍎1↓⎕←⍞: otherwise, read a line (⍞), echo it (⎕←), drop the first character (1↓), evaluate it to get a number (⍎), see where it occurs on the board (B=), mark it (⍵∨), and try again (∇). # K, 114 Given the board b and the calls c b{-1@y;a:(5*!5)_@[,/x;&(,/x)~\:1_y;:;"*"];$[max{max@min'"*"=,/'x}@/:(a;a ./:/:+:'(r;)'(r;|:r:!5));'"BINGO!";];a}/c


.

k)b
"14" "29" "38" "52" "74"
,"4" "18" "33" "46" "62"
,"7" "16" ,"*" "60" "71"
,"9" "27" "44" "51" "67"
"12" "23" "35" "47" "73"
k)c
"B7"
"I29"
"G60"
"G51"
"O71"
"I23"
"I16"
"N38"
k)b{-1@y;a:(5*!5)_@[,/x;&(,/x)~\:1_y;:;"*"];\$[max{max@min'"*"=,/'x}@/:(a;a ./:/:+:'(r;)'(r;|:r:!5));'"BINGO!";];a}/c
B7
I29
G60
G51
O71
I23
I16
'BINGO