20
\$\begingroup\$

Introduction

Connect Four is a game where you attempt to get four in a row: horizontally, vertically, or diagonally. In this code golf, we will be trying to find who won, given a game board. There will always be one winner, and only one winner.


Task

Given a Connect Four board, figure out who the winner is: X or Y. There will always be one winner, and only one winner. The board size will always be 6 by 7 like how the game board is in the in picture.

Given a board the following board, in this instance, X is red and Y is blue:

enter image description here

Your input would be:

OOOOOOO
OOOOOOO
OOOOOOO
OOOOXOO
OOOXXOO
OOXYYYY

You can separate rows of the game by newline character (like above), no dividing character, divide the rows into an array or list, or you can input a matrix of characters.

Correct output for this example:

Y

Y has four in a row; so, Y is the winner. So, we output Y.


Test cases

Input:

OOOOOOO
OOOOOOO
OOOOOOO
OOOOOOO
OOYYOOO
OYXXXXO

Output:

X

Input:

OOOOOOO
OOOOOOO
OOOOOOO
XXXXOOO
YXYYOOO
YXYYXYX

Output:

X

Input:

YXYYXOO
XYXXYOO
XXXYYOO
YYYXXOO
XXYYYYO
XXYYXXO

Output:

Y

Input:

OOOOOOO
OOOOOOO
OYOOOOO
OOYOOOO
OOOYOOO
OOOOYOO

Output:

Y

Input:

OOOOOOO
OOOOOOO
OYOOOOX
OOYOOOX
OOOXOOX
OXOXYOX

Output:

X

Scoring

Least number of bytes wins!

\$\endgroup\$
  • \$\begingroup\$ This is the perfect challenge for PMA/Snails codegolf.stackexchange.com/questions/47311/… \$\endgroup\$ – Jerry Jeremiah Apr 21 '17 at 1:21
  • 2
    \$\begingroup\$ Can we assume that the winner will always have one more token than the loser? \$\endgroup\$ – math junkie Apr 21 '17 at 2:38
  • 1
    \$\begingroup\$ @mathjunkie I was wrong, you can't assume that. \$\endgroup\$ – Neil Apr 21 '17 at 2:44
  • 3
    \$\begingroup\$ @nfnneil does the output have to be X or Y or can we choose two other consistent outputs to indicate the winner? \$\endgroup\$ – Martin Ender Apr 21 '17 at 5:51
  • 1
    \$\begingroup\$ Can we choose to use other characters as input? Or to input a numeric matrix? \$\endgroup\$ – Luis Mendo Apr 21 '17 at 7:42

10 Answers 10

2
\$\begingroup\$

Jelly, 19 bytes

UŒD;ŒD;Z;ṡ€4;/ṢEÞṪṪ

Try it online!

The core of this answer is copied from my answer to this very similar question.

Explanation

UŒD;ŒD;Z;ṡ€4;/ṢEÞṪṪ
   ;  ; ;             Append {the input} and the following three values:
UŒD                     the antidiagonals of {the input};
    ŒD                  the diagonals of {the input};
       Z                the transposed {input}.
         ṡ 4          Find all length-4 substrings
          €             of each subarray within that.
            ;/        Flatten one level.
                Þ     Sort, with the following sort order:
               E        If all elements are the same, sort later.
              Ṣ         Tiebreak via lexicographical order.
                 ṪṪ   Take the last element of the last element.

Fairly simple: we take all rows, columns, diagonals, and antidiagonals (just as in the n-queens validator), then take all length-4 substrings of those, then sort them in such a way that the winning line of 4 sorts to the end. (We need the tiebreak in case there's an OOOO in addition to the XXXX or YYYY.) Take the last element of the last element, and that'll be X or Y as required.

\$\endgroup\$
6
\$\begingroup\$

Retina, 51 48 bytes

Thanks to Martin Ender for saving 3 bytes

M`X((.{6}X){3}|(.{8}X){3}|(.{7}X){3}|XXX)
T`d`YX

Try it Online!

Takes input as a comma-separated list of rows

\$\endgroup\$
  • \$\begingroup\$ You can save a few bytes by using a match stage and shortening (.{7}X){3}|XXX to (.{7}X|X)\4\4: tio.run/nexus/retina#fc4xCsMwDAXQPfcI2GC6NDS5QaeipcP/… \$\endgroup\$ – Martin Ender Apr 21 '17 at 5:57
  • 1
    \$\begingroup\$ @MartinEnder I don't see how you can use \4 - you want to repeat the effect of the .{7}, not the matched string. (And balancing groups would probably be far too long.) \$\endgroup\$ – Neil Apr 21 '17 at 7:59
  • 1
    \$\begingroup\$ @Neil oh yeah, nevermind, somehow I didn't consider that there are other OXY cells than the match in the grid. using the match stage still saves 3 bytes then. \$\endgroup\$ – Martin Ender Apr 21 '17 at 8:00
5
\$\begingroup\$

Javascript (ES6), 54 55

Edit 1 byte saved thanks @Arnauld

I just check if X is the winner, as There will always be one winner, and only one winner

Input is a string with any separator, like in @Arnauld's answer

F=    
b=>'YX'[+[0,6,7,8].some(x=>b.match(`X(.{${x}}X){3}`))]

;['OOOOOOO OOOOOOO OOXOOOO OOXOOOO OOXOOOO OOXOYYY'
 ,'OOOOOOO OOOOOOO OOXOOOO OOYXOOO OOYOXOO OOYYOXY'
 ,'OOOOOOO,OOOOOOO,OOOOOOO,OOOOOOO,OOYYOOO,OYXXXXO'
 ,'OOOOOOO,OOOOOOO,OOOOOOO,XXXXOOO,YXYYOOO,YXYYXYX'
 ,'YXYYXOO,XYXXYOO,XXXYYOO,YYYXXOO,XXYYYYO,XXYYXXO']
.forEach(s => console.log(s,F(s)))

\$\endgroup\$
  • \$\begingroup\$ @Arnauld right, thanks \$\endgroup\$ – edc65 Apr 21 '17 at 10:11
4
\$\begingroup\$

Jelly, 25 22 bytes

ŒgL⁼¥Ðf
;UŒD€;Z;$ç€4FṀ

Takes a list of strings (or list of list of characters) formed of X, Y, and O (would also work with replacements such that the space has a lower ordinal than both counters).

Try it online! or run an augmented version that takes a multiline string.

How?

ŒgL⁼¥Ðf - Link 1, runs of given length: list A, length B  e.g. "XYYYXXO", 4
Œg      - group runs of equal elements of A                     ["X","YYY","XX","O"]
     Ðf - filter keep:
    ¥   -     last two links as a dyad:
  L     -         length                                         1   3     2    1
   ⁼    -         equal to B?         (none kept in this case->) 0   0     0    0

;UŒD€;Z;$ç€4FṀ - Main link: list of list of chars (or list of stings) I
 U             - reverse each row of I
;              - I concatenated with that
  ŒD€          - positive diagonals of €ach (positive and negative diagonals)
        $      - last two links as a monad:
      Z        -     transpose of I (i.e. the columns)
       ;       -     concatenated with I (columns + rows)
     ;         - concatenate (all the required directional slices)
         ç€4   - call the last link (1) as a dyad for €ach with right argument = 4
            F  - flatten the result
             Ṁ - take the maximum ('Y'>'X'>'O') - this has the bonus effect of returning:
                               'Y' or 'X' for a winning board; and
                               'O' or '' for a (valid) game in progress.
\$\endgroup\$
4
\$\begingroup\$

JavaScript (ES6), 77 76 69 bytes

Saved 7 bytes thanks to Neil

Takes input as a something-separated string, where something is basically any character.

b=>[...'XXXXYYYY'].find((c,i)=>b.match(`(${c}.{${(i%4+6)%9}}){3}`+c))

Test cases

let f =

b=>[...'XXXXYYYY'].find((c,i)=>b.match(`(${c}.{${(i%4+6)%9}}){3}`+c))

console.log(f("OOOOOOO,OOOOOOO,OOOOOOO,OOOOOOO,OOYYOOO,OYXXXXO"))
console.log(f("OOOOOOO,OOOOOOO,OOOOOOO,XXXXOOO,YXYYOOO,YXYYXYX"))
console.log(f("YXYYXOO,XYXXYOO,XXXYYOO,YYYXXOO,XXYYYYO,XXYYXXO"))

\$\endgroup\$
  • \$\begingroup\$ Why not use b.match()? Should save on the RegExp call. \$\endgroup\$ – Neil Apr 21 '17 at 8:04
  • \$\begingroup\$ @Neil I totally forgot that match() was doing an implicit conversion to RegExp. Thanks! \$\endgroup\$ – Arnauld Apr 21 '17 at 8:22
3
\$\begingroup\$

Python 2, 143 bytes

m=input()
u=[r[::-1]for r in m]
print"YX"[any(any('X'*4in''.join(t[i][j-i]for i in range(j+1))for j in range(6))for t in(m[::-1],m,u,u[::-1]))]

Takes a list of strings or a list of list of chars. Hard-coded for 6 rows by 7 columns, as the specification guarantees.

Try it online!

\$\endgroup\$
2
\$\begingroup\$

PHP, 71 Bytes

echo preg_match('#X(XXX|(.{8}X){3}|(.{7}X){3}|(.{9}X){3})#',$argn)?X:Y;

Online Version

\$\endgroup\$
2
\$\begingroup\$

Python 2, 201 143 129 128 107 Bytes

I decided to add horizontal, vertical, and diagonal together into one list and then add increment then look for X for times in it. And since there will always be a winner, I can assume Y won if X doesn't. This codes takes a matrix of all the different pieces and empty places.

lambda m:"YX"[any("X"*4in"".join(a)for a in zip(*m)+m+zip(*["0"*(7-i)+m[i]+"00"*i+m[i]for i in range(6)]))]

Try it online!

Credits

\$\endgroup\$
  • \$\begingroup\$ It's perfectly acceptable to self-answer. \$\endgroup\$ – Jonathan Allan Apr 21 '17 at 2:56
  • \$\begingroup\$ Without looking too much at it, there seems to be useless whitespaces at: i:] for, i, r, r] for and 1 for. \$\endgroup\$ – Yytsi Apr 21 '17 at 4:48
  • \$\begingroup\$ @TuukkaX Thanks for the input, updated. \$\endgroup\$ – Neil Apr 21 '17 at 4:50
  • \$\begingroup\$ Also, *(len(m)-1) could be *~-len(m). How it works. \$\endgroup\$ – Yytsi Apr 21 '17 at 4:54
  • \$\begingroup\$ The ] for and 1 for are still there. \$\endgroup\$ – Yytsi Apr 21 '17 at 5:02
1
\$\begingroup\$

K (ngn/k), 58 55 bytes

{"XY"@|/&/'88<x ./:/:,/{x+/:/:+3+[4#1-+!3 3]\&4}'+!6 7}

Try it online!

{ } function with argument x

+!6 7 all possible pairs of 0..5 and 0..6

{ }' for each of them do

4#1-+!3 3 are 4 of the 8 ortho-diagonal directions: (1 1;1 0;1 -1;0 1)

3+[ ]\&4 start with a list of four zeroes (&4) and make 3 steps in each of the directions

x+/:/: start from each possible position and take the steps in each possible direction

,/ concatenate. at this point we have a matrix of 4-lists of coordinate pairs, some of them extending beyond the board

x ./:/: look up the corresponding cells from x

88< which of them are "Y"-s? (88 is the ascii code of "X")

&/' which 4-lists consist only of "Y"-s? (and-reduce-each)

|/ is there at least one such? (or-reduce)

"XY"@ if false return "X", if true return "Y"

\$\endgroup\$
1
\$\begingroup\$

Zsh, 207 ... 159 bytes

Version history: 4 iterations for ~25 bytes first week; then 3 more iterations for ~25 bytes 6 months later.

t(){a=($^a-$^@_);for s l (${w:^^argv})s+=$l&&for ((i=0;i++<$#s;))a[i]+=$s[i];}
w=(+)
t $@
for s;w[++j]=${(l:j:)}_
t $@
t ${(Oa)@}
[[ $a = *XXXX* ]]&&<<<X||<<<Y

(first) (second) (third) (fourth) (fifth) (sixth) Try it online!

In the footer, I print both the input board and the array we build from it to stderr. Scroll down to debug to see them. The array we build is a lot longer now, since t does a cartesian product with input board on every call. (Hey, it shortened the code by a few bytes.)

There's a lot to cover here, so I moved the (sixth edition) comments to an annotated gist.

(tl;dr: concatenate transpositions of the original array, but make sure to keep them separated)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.