Display a Connect Four game

6

Connect Four is a game where two players take turns to drop disks into columns of a vertically mounted grid, and attempt to connect four disks orthogonally or diagonally. When a column is chosen, the disk falls to the lowest empty position in that column, so any game may be completely specified by the size of the grid and the sequence of columns chosen. The task is to generate the configuration of the grid given this information.

Input

  • Width w and Height h of the grid, both positive integers.
  • Sequence of columns c1, c2, ..., cn chosen by the players, in any reasonable format (list, array etc.); either 0-based (0 to w-1) or 1-based (1 to w) indexing can be used.

It may be assumed that all of the column numbers lie within the grid (so the sequence won't contain -3 or w+50) and that any column number appears at most h times in the sequence (so a column can't be chosen once it is full).

Note that the sequence of columns may not constitute a complete game, nor a legal game. For instance, the players could opt to continue playing even after a player has connected four disks.

Output

  • Any reasonable representation of the resulting state of each position in the grid, either "occupied by Player 1", "occupied by Player 2", or "empty". Player 1 always moves first.

For example, a string, array, tuple etc. with each position containing one of three symbols (ASCII character, number etc.) representing the state of that position in the grid. The grid may be transposed.

For list/tuple/array outputs, the separator cannot be one of the three representative symbols. For string outputs, trailing spaces at the end of each line are permitted, provided a space is not one of the three representative symbols. Trailing newlines at the very end are permitted.

Test cases

Here, string output is used, with X, O, . denoting "occupied by Player 1", "occupied by Player 2", "empty".

IN: Width 1 Height 1 Sequence (empty sequence)
OUT:
.

IN: Width 7 Height 6 Sequence 4 4 5 5 6 7 3 [1-based]
                              3 3 4 4 5 6 2 [0-based]
OUT:
.......
.......
.......
.......
...OO..
..XXXXO

IN: Width 5 Height 5 Sequence 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 [1-based]
                              0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 [0-based]
OUT:
XOXOX
OXOXO
XOXOX
OXOXO
xOXOX

IN: Width 4 Height 4 Sequence 3 3 2 4 3 2 4 2 2 3 4 4 1 1 1 1 [1-based]
                              2 2 1 3 2 1 3 1 1 2 3 3 0 0 0 0 [0-based]
OUT:
OXOO
XOXX
OOOX
XXXO

IN: Width 10 Height 10 Sequence 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 1 10 1 10  [1-based]
                                0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0  9 0  9  [0-based]
OUT:
X.........
X.........
X.........
X.........
X.........
X.........
X.........
X.........
X........O
XOOOOOOOOO

Shortest code in bytes wins.

for Monica

Posted 2016-10-17T12:35:10.360

Reputation: 1 172

A representation of the resulting state, with rows and columns switched, would be clearly understandable and reasonable, and SO easier to obtain – edc65 – 2016-10-17T13:16:42.740

Does it matter which player starts the game? – Kade – 2016-10-17T13:23:23.897

@edc65 You may print a transposed board, if that's what you are asking. – for Monica – 2016-10-17T13:30:08.007

@Shebang No it doesn't matter. Player 1 always moves first, label his disks with the same symbol. – for Monica – 2016-10-17T13:31:13.600

@lastresort Your first and last sentence seem to contradict. So any game can start with either O or X moving? – Kade – 2016-10-17T13:34:10.407

@Shebang Only consider Player 1 moving first. If you choose to label Player 1's disks with X, then the lowest position in the first column chosen is always marked X (unless it's an empty game, where no columns are chosen). – for Monica – 2016-10-17T13:50:16.200

Answers

1

Pyth, 19 bytes

m.[2vz%R2fq@QTd_UQE

Try it online.

Takes input as c,o,l,s\nheight\nwidth, 0-indexed. Outputs a list of columns (transposed), using 012 for player 1, player 2, empty respectively.

PurkkaKoodari

Posted 2016-10-17T12:35:10.360

Reputation: 16 699

2

Python 2, 91 88 bytes

w,h,s=input()
i,x=0,['']*w
for c in s:x[c]+='XO'[i%2];i+=1
for c in x:print(c+'.'*h)[:h]

Since transposed is fine, this prints the board sitting on its right hand side.
repl.it

Jonathan Allan

Posted 2016-10-17T12:35:10.360

Reputation: 67 804

The XO alternation can be done directly as for p,c in zip('XO'*w*h,s):x[c]+=p . – xnor – 2016-10-19T08:19:06.447

2

Vim, 34 bytes

@wO-<Esc><C-V>(d@hpqq<C-O>+D@"G+bs<C-R>=!@.<CR><Esc>@qq@q

Input is width in the "w register, height in the "h register, and the 1-indexed moves in the buffer, one per line. Output is a transposed grid with - as blank, 1 as player 1, and 0 as player 2 (with a bunch of newlines at the end).

To clarify, if you want Height 6, Width 7: type qh6qqw7q before starting.

  • @wO-<Esc><C-V>(d@hp: Use the height and width in the registers to make a transposed grid of -s. Note that ( is run from the last line of the grid, one above the moves. This sets up the jump list.
  • <C-O>+D@"G: The <C-O> uses the jump list to move back to before the last G, always to the line above the next number. @"G moves to the line number indicated by the deleted text. + not only moves the cursor, but it's the failure point; when it runs on the last line, the macro dies.
  • +b: This maneuver goes to the first - in the line, even if it's the first character. (f- would go to the second character if the line was still all -s.)
  • s<C-R>=!@.: Replaces a - with the last insert @., but NOTted. First time through, the previous insert of - will NOT to 1 because that's the way Vim is. After that, it will alternate between 0 and 1.

Example

Start with the moves in the buffer:

4
4
5
5
6
7
3

Make sure you're on line 1 with gg, clear "q with qqq, set up the registers for width 7, height 6 with qw7qqh6q. Then run the solution above and get the following output (with newlines at the end):

------
------
1-----
10----
10----
1-----
0-----

udioica

Posted 2016-10-17T12:35:10.360

Reputation: 2 381

0

Python 2, 143 131 107 Bytes

This doesn't use any real fancy tricks, except list transposition to make index access easier since we can print the board transposed. Definitely not done with this just yet. Moves are taken as 0-based numbers. Byte count comes before commenting.

Jonathan Allan had a better approach to building the board, mine is improved slightly since the slicing is a bit shorter [-j:] -> [:j] and the list comprehension helps shorten printing.

i,j,k=input()                                     # split up the input
t=0                                               # keep track of whose move it is
g=i*['']                                          # board init
for b in k:g[b]+='XO'[t%2];t+=1                   # read moves sequentially, place pieces
print'\n'.join(''.join((m+'.'*j)[:j])for m in g)  # build the board

Example Input: [7, 6, [3, 3, 4, 4, 5, 6, 2]]

Example Output:

......
......
X.....
XO....
XO....
X.....
O.....

Try it online or view all test cases.

Kade

Posted 2016-10-17T12:35:10.360

Reputation: 7 463

The ''.join is redundant, if you then move the print into the loop, you'll have the same as me. – Jonathan Allan – 2016-10-17T14:24:51.883

0

JavaScript (ES6), 62 69

Edit 7 bytes saved thx @Arnauld

I have used this kind of string representation when logging the board status in my connect four player. It's short coded and easy to understand at first sight.

Input is 0 based

(w,h,s)=>s.map((v,i)=>r[v]+=i&1,r=Array(w).fill`|`)&&r.join`
`

More in line with the OP examples, this is 93 bytes

(w,h,s)=>s.map((v,i)=>r[v]+=i%2,r=Array(w).fill(``))&&r.map(r=>r+Array(h+1-r.length)).join`
`

(note: here the fill`` trick won't work)

Output for [7, 6, [3, 3, 4, 4, 5, 6, 2]]

,,,,,,
,,,,,,
0,,,,,
01,,,,
01,,,,
0,,,,,
1,,,,,

Test

f=(w,h,s)=>s.map((v,i)=>r[v]+=i&1,r=Array(w).fill`|`)&&r.join`
`

console.log=s=>O.textContent+=s+'\n'

;[
  [1, 1, []]
, [7, 6, [3, 3, 4, 4, 5, 6, 2]]
, [5, 5, [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4]]
, [4, 4, [2, 2, 1, 3, 2, 1, 3, 1, 1, 2, 3, 3, 0, 0, 0, 0]]
].forEach(t=>{
  var w=t[0],h=t[1],s=t[2],r=f(w,h,s)
  console.log(w+' '+h+' ['+s+']\n'+r+'\n')
})
<pre id=O></pre>

edc65

Posted 2016-10-17T12:35:10.360

Reputation: 31 086

0

JavaScript (ES6), 121 bytes

(w,h,a,r=Array(w).fill``)=>a.map((c,j)=>r[c]+="XO"[j%2])&&[...Array(h)].map((_,i)=>r.map(s=>s[h+~i]||`.`).join``).join`\n`

0-indexed. I wanted to see how well I could match the example output, but most of the code seems to be taken up by the rotation.

Neil

Posted 2016-10-17T12:35:10.360

Reputation: 95 035

0

Java 7, 209 bytes

Not very inspiring.Simple and plain solution.

char[][]f(int w,int h,int[]a){char[][]b=new char[h][w];for(char[]r:b)Arrays.fill(r,'.');for(int l=a.length,i=l-1,j,c=0;i>-1;b[h-c-1][a[i]]=i--%2==0?'X':'O',c=0){for(j=i-1;j>-1 ;)if(a[j--]==a[i])c++;}return b;}

Ungolfed

 char[][] f( int w , int h , int[]a ) {
 char[][] b = new char [h][w];
for ( char[] r : b)
    Arrays.fill( r , '.') ;

for ( int l = a.length,i = l-1 , j , c = 0 ; i > -1 ;
                         b[h-c-1][a[i]] = i--%2 == 0 ? 'X' : 'O', c = 0 ) {
    for ( j = i-1 ; j > -1 ;)
        if ( a[j--] == a[i] )
              c++ ;
      }

return b;
}

Numberknot

Posted 2016-10-17T12:35:10.360

Reputation: 885

0

Scala, 122 bytes

def%(w:Int,h:Int,m:Int*)=((Seq.fill(w)(Nil:Seq[Int]),1)/:m){case((a,p),x)=>(a.updated(x,a(x):+p),-p)}._1.map(_.padTo(h,0))

Ungolfed:

def f(w:Int,h:Int,m:Int*)=
  m.foldLeft((Seq.fill(w)(Nil:Seq[Int]),1)){
    case((res,player),x)=>
      (res.updated(x,res(x):+player), -player)
  }._1.map(_.padTo(h,0))

Explanation:

def%(w:Int,h:Int,m:Int*)=        //define a method
  (
    (                            //with a tuple of
      Seq.fill(w)(Nil:Seq[Int]),   //a Sequence of w empty sequences
      1                            //and 1
    )                            //as a start value,
    /:m                          //foldLeft the moves
  ){                             //using the following function,
    case((a,p),x)=>                //which takes the result, the player and the x coordinate
      (                            //return a tuple of
        a.updated(x,a(x):+p),        //the seq with index x with p appended
        -p                           //and the other player
      )
  }._1.map(                      //take the resulting seq and foreach column
    _.padTo(h,0)                 //append 0 until the length is h
  )

Returns a sequence of the columns in bottom-to-top order. The players are represented as -1 and 1, empty cells are 0.

corvus_192

Posted 2016-10-17T12:35:10.360

Reputation: 1 889