1

I'm writing a Connect Four script. The script takes a list of moves as input ("A_Red, "B_Yellow", "G_Red", etc), which I'm sorting into a matrix. Each nested list represents a column of the board with "A" being the first nested list (or column), and "G" being the last nested list (or column). Moves will then get sorted like so:

A board with some moves will resemble something like:

[ ["Red, "Yellow"], ["Red"], ["Yellow", "Yellow"], [], [], [], [] ]

Once all of the moves are sorted into their respective nested list, I fill each nested list with empty strings ("") to prevent any 'list index out of range errors' with for loops used to perform 4-in-a-row checks.

How can I check if the 4th element (index[3]) of each nested list contains either "Red" or "Yellow" WITHOUT a for loop?

If this condition is True, the diagonal checks will then be performed, and I only need them performed once.

board = [["Red", "Yellow", "Red", "Yellow"], [], [], ["Red", "Yellow", "Red", "Yellow"], [], [], []]
for i in range (0, 7):
   if board[i][3]:
       # Perform Diagonal Check

The issue with the above code is that I only need the diagonal check to be performed one time when any "Red" or "Yellow" piece is identified in the fourth position in any nested list. With the above for loop, the check will be performed 7 times for each item in the fourth position.

Is it possible to check a range of list positions without going:

if board[0][3] or board[1][3] or board[2][3]... == "Yellow" or "Red": #etc...
CunnyFunt
  • 105
  • 7
  • I'm not sure why beginning Python programmers often develop an obsession with avoiding `for` loops? Is there some online curriculum telling you that `for` is evil? It's true that there may often be ways of doing something that are more efficient than writing a `for` loop, but sometimes such a loop is just what's needed. Here, if you're using basic lists, there are better solutions than what you're doing, but `for` is likely to show up. In most cases where people will tell you `for` is bad, the alternative tends to be to use something optimised hidden away in a library like `numpy`. – Grismar Jan 12 '23 at 22:47
  • Commenting on the actual question: you're not use arrays but lists here, so your question should probably be updated to reflect that, or you should share more of your code that shows how you use actual arrays. If you are in fact using lists, there are nicer and more efficient ways to write a check like the one you're describing, but they will include `for` - however, it would make more sense to look at the actual problem you're trying to solve, which is deciding whether 4 have been connected yet? – Grismar Jan 12 '23 at 22:50
  • I honestly have no issue with for loops, and I actually love using them - probably one of my favourite things if I'm being honest. The issue with using a for loop is I don't know how to use it in such a manner that will only perform the diagonal check once. For instance, if board[0][3] is "Yellow" and board[1][3] is "Red", then the diagonal check will be performed twice for each piece present in the 4th position of each column. Unless there's another way to do it. – CunnyFunt Jan 12 '23 at 23:11
  • I've already programmed the diagonal checks, which work. I'm trying to make it more efficient. Two of the ways I'm doing that are: 1) If less than 10 moves have been made, skip diagonal checks, and 2) if there are no pieces in the 4th row, skip diagonal checks. These are conditions necessary for a diagonal 4-in-a-row win to be possible. I'm trying to check that there are no pieces in the 4th row before performing a diagonal check. And I'm happy to use a `for` loop if there's a way to stop it from performing diagonal checks for each piece present in the 4th row. – CunnyFunt Jan 12 '23 at 23:11
  • If efficiency is the most important aspect, you could simply only perform checks that involve the last played piece - after all, the only 4s will involve that piece, otherwise the game would have ended sooner. If you have a situation where you have to start with a board in an ongoing game, and you *cannot* get data about what column was played last, then you have to wonder if performing a lot of logic to avoid doing more checks is any more efficient than just performing a really efficient check of the entire board. – Grismar Jan 12 '23 at 23:18
  • Another consideration, if you don't know what the last piece played is, you can limit the search to only those including a stone in the top of each column, and you can start with the topmost ones, only looking down and to the side (which means you can also stop looking for diagonals and verticals once you reach the bottom three rows) - unless you *also* have to check for illegal boards (i.e. games that ended previously, but somehow players kept playing), but that's a different problem altogether. – Grismar Jan 12 '23 at 23:27
  • Yep, I see what you mean by the whole board thing. Unfortunately we don't know the last played piece is - we're just given a 'snapshot' of a finished board and have to return "Yellow", "Red", or "Draw" for the winning outcome. There is only one winner in each game. Another efficiency I've made is to check diagonals from the center piece - every diagonal win has to cross through the center piece, so diagonal checks are based on that. If only 4 layers - diagonals starting from bottom center are checked. If 5 layers, then diagonals crossing through 2nd bottom center piece are also checked etc. – CunnyFunt Jan 12 '23 at 23:34
  • So, is a situation where a winning move was made three moves ago, but players just kept playing an option? Or does a game end by definition when a winning move is made, even if the players didn't realise it? – Grismar Jan 13 '23 at 00:28
  • Oh wait, I understand what you're saying - as in, the last move is the winning move (unless the board is full and there's a draw). That's a great idea, however I don't know which pieces are added at what point in the game. – CunnyFunt Jan 14 '23 at 01:57
  • Is there a way to reference a range of list items? E.g., ` my_list = [[1, 0, 2], [3, 0, 4], [5, 0, 6]] ###### if my_list[range(0,3)][1] == 0: print("All second elements are 0")` – CunnyFunt Jan 14 '23 at 01:57

1 Answers1

0

To check the 4th element in each sub-list, use the following code:

mylist = [[1, 2, 3, 0, 4], [5, 6, 7, 0, 8], [9, 1, 2, 0, 3]]

fourth_element_check = [x[3] for x in mylist]

if 0 in fourth_element_check:
    print ("0 is the fourth element of one of the nested lists")
CunnyFunt
  • 105
  • 7