Trying to check for ship duplicates running into problem


#1

While writing code for Battleship extra credit I ran into a problem trying to check for ship tile duplicates. The issue looks something like this:

def B_setup():
    B = [(1,randint(0,3)),(1,randint(0,3))]
    return B

A = [(1, 0), (1, 1), (1, 2)]

B = [(1, 0), (1, 1)]

for point in B:
    while point in A:
        B = B_setup()

print B

This outputs nothing and gives me no error to work with. When I try to close the shell it says it’s still running.
I expect

B = [(1, 3), (1, 3)]

Because those are the only points that randint can chose from that aren’t in A


Solution:

Bnew = []

for num in range(len(B)): #ensures num relates to B
    while B[num] in A: 
        B = B_setup() #sets B to a new list which changes B[num]
    Bnew.append(B[num])

if Bnew != []:
    B = Bnew

I was able to apply this code to my Battleship game. Thank you to post contributors for helping me.


#2

You don’t change point and you don’t change A
Those are all the variables in the condition
Since nothing changes, the loop runs forever (or not at all)

If you don’t see enough information, print out some more.


#3

This is some silliness that takes two lists of tuples and merges them, then ignores duplicates in a new list. The return is True, but False if there are intersections.
A and B are those in the OP.

>>> def no_intersections(p, q):
	c = []
	for x in p + q:
		if x not in c:
			c += [x]
	return len(c) == len(p + q)

>>> no_intersections(A, B)
False
>>> 

#4

sets already do this:

def no_intersections(p, q):
    return set(p).isdisjoint(set(q))

Or for that matter:

def no_intersections(p, q):
    return not set(p).intersection(q)

(computes intersection and checks whether empty)

Same as the last:

def no_intersections(p, q):
    return not (set(p) & set(q))

#5

Thanks for that! It is what I was emulating, without reading up on set methods, which only too obviously fit the bill.

This is what discouraged me, when it should have induced me to seek out the docs…

>>> set([A+B])
Traceback (most recent call last):
  File "<pyshell#238>", line 1, in <module>
    set([A+B])
TypeError: unhashable type: 'list'
>>> 

#6

Concatenating them and checking lengths would cause false positives (can’t tell if repeated element in same group or exists in both)

Anyway, you put the list of elements in a list: [C] instead of C


#7

D’oh!

>>> set(A+B)
{(1, 2), (1, 0), (1, 1)}
>>> 

This maintains duplicates…

>>> A+B
[(1, 0), (1, 1), (1, 2), (1, 0), (1, 1)]

Edit.

Okay, I see what you mean. However, if these coordinates are generated by the program, we can trust that the same coordinate wouldn’t be given to a ship that is being created.

Really appreciate the added insight. Thanks.


#8

Why does point not change if B is set to a different value? Since point relates to B I thought that would be the case.

Regardless, thanks for your help. I’m too tired to keep looking at this, I’ll try again tomorrow on a fresh brain.


#9

Not an answer to the above, but a question on the overall parameters… Why are all the coordinates constrained to the first row?


#10
a = 3
b = 7
# does a still refer to 3, or did changing b also change a?

If you’re thinking of variables as blocks of memory, and that point would refer to a location in memory, and that B refers to an area in memory that includes point

Then you’d run into trouble here:

a = range(5)
b = a
a = 'aoetnuhao'  # does this affect b?

No, variables are not storage.

Variables behave like a dictionary (each name a key). Assignment never writes any data, never copies. Assignment changes what a variable refers to.

And, if you do:

a = b

Then a does not refer to b.
a and b both refer to the same value.


#11

Starting to sink in…

>>> a = list(range(5))
>>> b = a
>>> b
[0, 1, 2, 3, 4]
>>> a[4] *= 2
>>> b
[0, 1, 2, 3, 8]
>>> a = 'a'
>>> b
[0, 1, 2, 3, 8]
>>> 

Reassigning a just means a can no longer have an effect on the same object as b.


#12
for point in B:
    while point in A:
        point = (1,randint(1,3))  #still working on how to incorporate B_setup() func
        if point not in A:
            B += [point]

for point in B:
    if point in A:
        B.remove(point)
for point in B:
    if point in A:
        B.remove(point)

print B

So I got this to output B = [(1, 3), (1, 3)]
It’s really ugly.
I understand now why I have to change point. Thanks for that.
I don’t understand why I have to use two for loops with B.remove(point) In order to remove the two duplicates. I know B.remove(point) only removes the first instance of point but since I’m doing that for every point in B I thought I would just need one.

Y’all deal with some loaded questions. Thanks for taking the time to help people that are new to this.
I feel confident that I will fully understand this after fiddling around with it a bit longer.


#13

I won’t speak for @ionatan, since that’s his reply to make, but I will say I’m glad you see how loaded up things are. There are a lot of considerations and avenues of approach. That’s what makes programming such a challenge and a joy to behold. Start to think like the machine we create and we learn to program it.


#14

Haiku (with added syllable)

Start to think like the 
machine we created and we
learn to program it

#15

Not directed at you though. It’s the expectation that point was somehow looking inside the variable B, except there’s no “inside” since it’s not storage


#16

You’ll express things better with more experience

Removing from a list is rarely what you want, it’s a strange thing to do for a list.

If you were, then yes, but you’re not. You’re removing stuff from the list you are iterating through, moving things in the list, and therefore skipping some. To process each one you would need to not change what you’re looping through (or ensure that changes do not affect what you’re about to visit)

Doing it twice is incredibly arbitrary. If you missed some the first time you may very well miss some next time too.


#17

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.