Discuss Scratch

Leonkghunt
Scratcher
87 posts

how do i make a win detector for connect 4?

im working on a board games game, one is connect four. i have done the place scripts but i can't find out how to make it detect if you win without adding a script that checks EVERY SINGLE SPOT you can possible win. this method would work for naughts + crosses but connect 4 has way too much spots. link https://scratch.mit.edu/projects/691213617/
deck26
Scratcher
1000+ posts

how do i make a win detector for connect 4?

You need a grid system - https://scratch.mit.edu/projects/75428464/ may help although it doesn't detect diagonal lines.

Essentially the method would be to start with the bottom left position and, if occupied, check for an adjacent set of 3 matches to the right and above. Also check diagonals going up and left and up and right but watching out for edges. Then you move on the second position and repeat and so on. Once you're checked the bottom row do the same for the second row. That way you never need to check for anything but a line starting with the current position and checking in directions pointing away from the positions already checked. In fact you can reduce the checking by seeing if the current position matches an adjacent one that has already been checked. There's no need to check for a vertical line, for example, if the position below matches the current position since that line would already have been checked.
RT_Borg
Scratcher
1000+ posts

how do i make a win detector for connect 4?

Hi Leonkghunt,

deck26 has put you on the right track, to use a grid system. I think you can make the checking a little easier on yourself, though.

Really, you don't have to check the entire board for a win every time a spot is filled. You only have to check whether the new spot completes a connect-4. (Any other would have already been detected on the turn it was played, and ended the game.)

So you can look around only the newly filled spot.
  • downward (of the same color) of 3 (plus the new spot) is a vertical connect-4
  • leftward count + rightward count (of the same color) of 3 or more (plus the new spot) is a horizontal connect-4
  • north-east count + south-west count of 3 or more => connect-4
  • north-west count + south-east count of 3 or more => connect-4

I hope this helps. Happy to answer any questions.

– RT_Borg
poke_cat_guy123
Scratcher
61 posts

how do i make a win detector for connect 4?

when green flag clicked
forever
if <> then
<[] = [4]>
broadcast [ v]
end
end
hope i did the blocks right
Leonkghunt
Scratcher
87 posts

how do i make a win detector for connect 4?

RT_Borg wrote:

Hi Leonkghunt,

deck26 has put you on the right track, to use a grid system. I think you can make the checking a little easier on yourself, though.

Really, you don't have to check the entire board for a win every time a spot is filled. You only have to check whether the new spot completes a connect-4. (Any other would have already been detected on the turn it was played, and ended the game.)

So you can look around only the newly filled spot.
  • downward (of the same color) of 3 (plus the new spot) is a vertical connect-4
  • leftward count + rightward count (of the same color) of 3 or more (plus the new spot) is a horizontal connect-4
  • north-east count + south-west count of 3 or more => connect-4
  • north-west count + south-east count of 3 or more => connect-4

I hope this helps. Happy to answer any questions.

– RT_Borg
Hey! sorry for the late response. but I don't know how to make it detect if it is touching 3 of the same type of counters?
RT_Borg
Scratcher
1000+ posts

how do i make a win detector for connect 4?

The link in your first post doesn't seem to be shared?

How to Share a Project Link
To share a project link, click the orange “Share” button at the top of the page. Then in “See Project Page” click Copy Link near the bottom, and Copy Link again on the pop-up, and paste it in a message.

– RT_Borg
Leonkghunt
Scratcher
87 posts

how do i make a win detector for connect 4?

RT_Borg wrote:

The link in your first post doesn't seem to be shared?

How to Share a Project Link
To share a project link, click the orange “Share” button at the top of the page. Then in “See Project Page” click Copy Link near the bottom, and Copy Link again on the pop-up, and paste it in a message.

– RT_Borg
Sorry, its shared now. also you may notice that the counters are getting stuck at the top, but that's just because im working on a new row finder method that's not finished.
NeonG4
Scratcher
1000+ posts

how do i make a win detector for connect 4?

Leonkghunt wrote:

RT_Borg wrote:

The link in your first post doesn't seem to be shared?

How to Share a Project Link
To share a project link, click the orange “Share” button at the top of the page. Then in “See Project Page” click Copy Link near the bottom, and Copy Link again on the pop-up, and paste it in a message.

– RT_Borg
Sorry, its shared now. also you may notice that the counters are getting stuck at the top, but that's just because im working on a new row finder method that's not finished.
This might seem complex, but you need to store every tile in a list, then scan the list for when there are 4 tiles in a row.
cIoudyness
Scratcher
500+ posts

how do i make a win detector for connect 4?

let’s assume your grid is 7x6 like standard
your grid list will obviously have 42 values.

first for new coin (or piece) placing,
here’s the snap-to x distance script, something like this:
set [var v] to (round ((x position) / (7)))
set x to (((var) * (7)) + (0)) // experiment with this 0 to get an offset that you like
conveniently the x snapping also gives you a variable that tells you the lane of the piece that went in. (you may need to add 3 or 4 to it to give you a lane 1-7)

then maybe to record the piece placed you can do something like this:
find empty space

define find empty space
set [i v] to (0) // local variable
check
if <(i) = (-1)> then
invalid column: already filled to the top! ::sensing
broadcast [restart turn v]
stop [this script v]
end
replace item ((lane) + ((i) * (7))) of [list v] with (player this turn) // perhaps 1 for red or 2 for blue
set y to ((150) - ((i) * (30))) // experiment with values. may be very far off

define check
if <(item ((lane) + ((i) * (7))) of [list v]) = (0)> then
check
else
change [i v] by (-1)
stop [this script v]
end
will code something for checking for a win in a moment. if something doesn’t work pls tell

Last edited by cIoudyness (July 11, 2022 18:39:13)

cIoudyness
Scratcher
500+ posts

how do i make a win detector for connect 4?

I know I promised a win detector earlier. still working

if anyone else can help, we’re trying to check seven spots in a line, from a grid list with the new tile at the center of said seven spots. we need to do this for up-down, top right to bottom left, right to left, and bottom right to top left.
cIoudyness
Scratcher
500+ posts

how do i make a win detector for connect 4?

first let’s set some variables to more easily comprehensible names. of course the win detection script should go right after the new piece has been placed (put this code right after the previous script sets y, or better yet, set your variables to these names at the start to avoid confusion)

set [opposite color v] to ((1) - (player that just moved))
you may just want to set this with an if player that just moved is 1, then opposite is 2. else vice versa
for this you need to use 0 for one side, 1 for the other and use the empty value “” to represent nothing being in a certain list value. so when you set your list fill it with 42 empty values.
set [column v] to (lane)
set [row v] to (i) // worth noting that this is actually 1 less than the perceived row we see. it makes calculation easy
find spot
set [spot v] to (i)
check win?

define find spot
set [i v] to (((row) * (7)) + (column))

define check win
set [checking v] to (0)
repeat (4)
set [x change v] to ([sin v] of (checking))
set [y change v] to (round ([cos v] of (checking)))
set [count v] to (0)
try for max (3)(1)
find spot
set [x change v] to ((0) - (x change))
set [y change v] to ((0) - (y change))
set [longest string v] to (0)
try for max ((count) + (4))(-1)
if <(longest string) = (4)> then
stop [this script v]
// we’ve already broadcasted this to other sprites in the code below
end
change [checking v] by (45)
end

define try for max (input)(count #)
repeat (input)
change [count v] by (count #)
change [column v] by (x change)
change [row v] by (y change)
if <(count #) = (-1)>then
find spot
if <(item (i) of [list v]) = (player we’re checking for right now::sensing)> then
change [longest string v] by (1)
else
set [longest string v] to (0)
end
if <(longest string) = (4)> then
broadcast [yay there’s a winner! v]
stop [this script v]
end
if <not <(longest string) > (count)>> then
stop [this script v]
end
if <<(row) < (0)> or <(row) > (6) >> then
opposite move // this has another use elsewhere
stop [this script v]
end
if <<(column) < (0)> or <(column) > (7)>> then
opposite move // here just returns to the last position
stop [this script v]
end
find spot
if <(item (i) of [list v]) = (opposite color!!! :: sensing) > then
opposite move
stop [this script v]
end

define opposite move
change [column v] by ((0) - (x change))
change [row v] by ((0) - (y change))

that should be it? not really any chance that all this code works but the point is to show the process:
get your row, your column and your spot, then
check 4 of the 8 directions. take 3 steps up (if possible) then go down 7, checking for consecutive player piece stings along the way. do the same for two diagonals and the horizontal axes.

most of these variables should be made ‘for this sprite only’

any questions or something isn’t working (very likely) please let me know.

Last edited by cIoudyness (July 11, 2022 22:20:44)

cIoudyness
Scratcher
500+ posts

how do i make a win detector for connect 4?

I’ve just noticed this is an insane amount of scratchblocks. (sorry in advance to any mobile users) just didn’t feel like coding a connect 4 project myself.

of course once you implement all this code I should bug fix it again in an actual project
RT_Borg
Scratcher
1000+ posts

how do i make a win detector for connect 4?

(Edit: I see cIoudyness has posted logic like what you'd use with this, while I was writing my message.)

Hi Leonkghunt and cIoudyness,

For most problems like this…

I would create a list to store all the states of the slots in the board. The first column (first 6 items) in positions 1-6. The second column (next 6 items) in positions 7-12, etc.

(board :: list)
when green flag clicked
delete all of [board v]::list
repeat (42) // 7 columns * 6 rows
add [EMPTY] to [board v]
end

Then create custom blocks to make it easy to store and retrieve these states

define Set State | row: (row) col: (col) state: (state)
set [list-index v] to ( (row) + (((col) - (1)) * (6)))
replace item [list-index v] of [board v] with (state)

(state) // most recent state from Get State

define Get State | row: (row) col: (col) |> state
set [list-index v] to ( (row) + (((col) - (1)) * (6)))
set [state v] to (item [list-index v] of [board v])

Now that it's easy to get and set states (e.g., “PLAYER-1” or “PLAYER-2”) the logic to look for states relative to a given position should be more straightforward.

– RT_Borg


Last edited by RT_Borg (July 11, 2022 22:35:49)

RT_Borg
Scratcher
1000+ posts

how do i make a win detector for connect 4?

All that said (and I think it's a lesson worth the time, whether you use it here or not)… for this problem:

I made a remix of the project, and a Detector sprite with costumes that draw lines in each direction.

FOR HELP: Board Games remix
https://scratch.mit.edu/projects/713449653

If centered at a position on the board, each of these costumes will draw a line that overlaps 4 slots in the costume direction (that will make sense if you look at Detector in the remix).

Assuming you can easily position Detector at the spots on your board, move it to each spot. On the spot, switch to each costume.

Create counters:
(TOUCHING-RED-COUNT) // For All Sprites
(TOUCHING-YELLOW-COUNT) // For All Sprites

Then for each spot/costume, you'll get the Indicator clones to do the counting for you.

set [TOUCHING-RED-COUNT v] to (0)
broadcast [Count Red v] and wait
if <(TOUCHING-RED-COUNT) > (3)> then
... / win for red. broadcast. stop script
end
...
set [TOUCHING-YELLOW-COUNT v] to (0)
broadcast [Count Yellow v] and wait
if <(TOUCHING-YELLOW-COUNT) > (3)> then
... / win for yellow. broadcast. stop script
end

In Connect 4 Counter (which is also the sprite that represents token clones):

Edit: I first forgot to check “Costume name = Red/Yellow” when receivers change these counts. (And really, both counts should probably just get updated with a single broadcast.)

when I receive [Count Red v]
if <((costume [name v]::looks) = [Red]) and (touching [Detector v] ?)> then
change [TOUCHING-RED-COUNT v] by (1)

when I receive [Count Yellow v]
if <((costume [name v]::looks) = [Yellow]) and (touching [Detector v] ?)> then
change [TOUCHING-YELLOW-COUNT v] by (1)

Anyway, I wanted to share this alternate way to think about solving the problem.

If you want to try this, but can't find a good way to position the Detector and initiate the counting, let me know and I'll share some ideas about that.

– RT_Borg

Last edited by RT_Borg (July 12, 2022 03:03:54)

cIoudyness
Scratcher
500+ posts

how do i make a win detector for connect 4?

= RT_Borg wrote:

snip
now that I’ve realized how utterly dumb using code alone is,
perhaps a teeny suggestion is to just have two costumes for straight and diagonal and just turn to the currently checked direction

very inconsequential though

Last edited by cIoudyness (July 12, 2022 02:24:51)

RT_Borg
Scratcher
1000+ posts

how do i make a win detector for connect 4?

cIoudyness wrote:

…how utterly dumb using code alone is

Well starting from lists and positions, like you and I both approached it, could simplify some different things, like where tokens should be dropped from and where they should land. It also generalizes better to other board types or similar games.

cIoudyness wrote:

perhaps a teeny suggestion is to just have two costumes for straight and diagonal and just turn to the currently checked direction

Where were you when I was flipping and moving costume lines Yes, that would have been easier, especially since the costumes I made have the proper rotation point to do that.

I forgot to mention the thing I liked the most when I was inspired by this technique vs lists. When you find a win, the Detector positioned across 4 in a row would be a nice display to the user.

– RT_Borg
cIoudyness
Scratcher
500+ posts

how do i make a win detector for connect 4?

RT_Borg wrote:

cIoudyness wrote:

…how utterly dumb using code alone is

Well starting from lists and positions, like you and I both approached it, could simplify some different things, like where tokens should be dropped from and where they should land. It also generalizes better to other board types or similar games.

cIoudyness wrote:

perhaps a teeny suggestion is to just have two costumes for straight and diagonal and just turn to the currently checked direction

Where were you when I was flipping and moving costume lines Yes, that would have been easier, especially since the costumes I made have the proper rotation point to do that.

I forgot to mention the thing I liked the most when I was inspired by this technique vs lists. When you find a win, the Detector positioned across 4 in a row would be a nice display to the user.

– RT_Borg
as I remember you’re checking 4 units radiating outward from the newly placed piece. but imagine the uncommon edge case where the new piece completes a four, but not at the edges - imagine a set of stairs missing a step, and we fill in that step! checking starting from the new stair will only show us, say, three including the new piece up, and two including the new piece down

forgive me, but is that a fatal flaw within otherwise flawless and well-reasoned coding?? :O
Leonkghunt
Scratcher
87 posts

how do i make a win detector for connect 4?

Hey! I think i created a way to do it, i just need to actually code it in.
Leonkghunt
Scratcher
87 posts

how do i make a win detector for connect 4?

Leonkghunt wrote:

Hey! I think i created a way to do it, i just need to actually code it in.
Nevermind, It Didnt Work, And I Found A Flaw.
RT_Borg
Scratcher
1000+ posts

how do i make a win detector for connect 4?

cIoudyness,

If you're using the a list-based method, you only have to check the possibility that include the new spot (not necessarily as an endpoint). Technically it's the same for the touching Detector approach, but probably harder to select the subset to check. That's why I suggested just checking each costume position at every x,y on the board (or at least every one where a token is already placed).

Really, this should all happen so fast, there's no reason to optimize if it takes extra blocks to program.

Leonkghunt,

The easiest way to implement the touching Detector way is probably to store the x,y positions you need the detector to position itself at.

(DETECT_X :: list) // For all sprites
(DETECT_Y :: list) // For all sprites

On green flag clicked, delete all, in case the player has entries from a previous playthrough.

You can either fill these x,y positions all in at the start of the game, or have tokens add their x, y to the lists when they get placed on the board.

Then, wherever in the code you determine a win check should be made,

broadcast [Detect Win v] // possibly "and wait", depending on where this is

The only receiver for Detect Win is Detector (the new sprite with the lines in each direction).

When Detector receives “Detect Win” it loops through each index (repeat the length of either list, they're the same length). For each x, y pair from the lists, Detector moves to that position,

define Detect // Run without screen refresh
show
set [i v] to (1)
repeat (length of [DETECT-X v] :: list)
go to x: (item (i) of [DETECT-X v] :: list) y: (item (i) of [DETECT-Y v] :: list)
... // below
end
hide

In a nested loop, it loops through each of its 8 costumes (pointing in each direction) and on each costume uses broadcasts to find if there's a winner

// the nested part
switch costume to [north v]
repeat (8)
set [TOUCHING-RED-COUNT v] to (0)
set [TOUCHING-YELLOW-COUNT v] to (0)
broadcast [Check Touching Detector v] and wait // the clone at ROW, COLUMN will do a test
if <(TOUCHING-RED-COUNT) > (3)> then
... / win for red. broadcast. stop script
end
if <(TOUCHING-YELLOW-COUNT) > (3)> then
... / win for yellow. broadcast. stop script
end
next costume
end

In Connect 4 Counter (the sprite that represents token clones):

when I receive [Check Touching Detector v]
if <((costume [name v]::looks) = [Red]) and (touching [Detector v] ?)> then
change [TOUCHING-RED-COUNT v] by (1)
end
if <((costume [name v]::looks) = [Yellow]) and (touching [Detector v] ?)> then
change [TOUCHING-YELLOW-COUNT v] by (1)
end

If there's a non-clone Connect 4 Counter, you'll have to do something to make sure it doesn't record itself as touching the Detector. Either a clone-id type thing, or make sure it has a different costume to wear when it isn't being used as a template to make a clone. (Happy to suggest if you aren't sure what to do.)

I think that basic idea should work (though it's possible I missed a detail along the way).

– RT_Borg

Powered by DjangoBB