Skip to content

Conversation

@drvinceknight
Copy link
Member

  • Add equality method and tests to player.
  • Add documentation for player equality.
  • Refactored some other tests to make use of this method
  • Refactor a match generator test which was failing as a result of the
    eq method. Previously this was testing equality of instance.

Some other minor changes I've made as a result of this:

  • Change when qlearner makes random choice. I've also removed the reset
    test. This is now being tested by the parent test class for all
    strategies.
  • Fix small bug in contrite. Without this, contrite was not resetting
    correctly (the recorded history was being maintained in the class
    attribute).

This closes #885 but also closes #940 which was worked on a while ago
but which has not seen any progress.

setattr(other, attribute,
(ele for ele in original_other_value))

if not (all(next(generator) == next(other_generator)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could cause unintended behavior if used during a match or tournament.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see what you mean?

I've done this in such a way that the generator gets copied and there are tests that show that using __eq__ does not alter the generator.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lines 199 and 215:

187     def test_equality_for_generator(self):                                                                                                        
188         # Check generator attribute (a special case)                                                                                              
189         p1 = axelrod.Cooperator()                                                                                                                 
190         p2 = axelrod.Cooperator()                                                                                                                 
191         # Check that the generator is still the same                                                                                              
192         p1.generator = (i for i in range(10))                                                                                                     
193         p2.generator = (i for i in range(10))                                                                                                     
194         self.assertEqual(p1, p2)                                                                                                                  
195                                                                                                                                                   
196         _ = next(p2.generator)                                                                                                                    
197         self.assertNotEqual(p1, p2)                                                                                                               
198                                                                                                                                                   
199         # Check that internal generator object has not been changed                                                                               
200         self.assertEqual(list(p1.generator), list(range(10)))                                                                                     
201         self.assertEqual(list(p2.generator), list(range(1, 10)))                                                                                  
202                                                                                                                                                   
203     def test_equality_for_cycle(self):                                                                                                            
204         # Check cycle attribute (a special case)                                                                                                  
205         p1 = axelrod.Cooperator()                                                                                                                 
206         p2 = axelrod.Cooperator()                                                                                                                 
207         # Check that the cycle is still the same                                                                                                  
208         p1.cycle = itertools.cycle(range(10))                                                                                                     
209         p2.cycle = itertools.cycle(range(10))                                                                                                     
210         self.assertEqual(p1, p2)                                                                                                                  
211                                                                                                                                                   
212         _ = next(p2.cycle)                                                                                                                        
213         self.assertNotEqual(p1, p2)                                                                                                               
214                                                                                                                                                   
215         # Check that internal generator object has not been changed                                                                               
216         self.assertEqual(next(p1.cycle), 0)                                                                                                       
217         self.assertEqual(next(p2.cycle), 1)  

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So when the generator is copied it's state is also reset?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup that's right. What itertools.tee does is essentially fork a generator in to two versions of itself from the state it currently is in.

That is also being tests:

  1. Create two Cooperators and give them two cycle generators and tests that they are equal (at this stage their generator are in the same state:

    205         p1 = axelrod.Cooperator()                                                                                                                 
    206         p2 = axelrod.Cooperator()                                                                                                                 
    207         # Check that the cycle is still the same                                                                                                
    208         p1.cycle = itertools.cycle(range(10))                                                                                                    
    209         p2.cycle = itertools.cycle(range(10))                                                                                                    
    210         self.assertEqual(p1, p2)   
    
  2. Bump p2s cycle and now the players are not the same:

    212         _ = next(p2.cycle)                                                                                                                       
    213         self.assertNotEqual(p1, p2)  
    
  3. Check what the next values in the cycle are for each player, for p1 it should be 0 because the player has not iterated through any values and for p2 it should be 1 because it has iterated through the first value:

    215         # Check that internal generator object has not been changed                                                                              
    216         self.assertEqual(next(p1.cycle), 0)                                                                                                       
    217         self.assertEqual(next(p2.cycle), 1)  
    

Let me know if you think I could clarify that in the code and/or add other tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI test_equality_on_init is the test that fails in this case.

This should clarify why:

>>> import itertools
>>> import types
>>> cycle = itertools.cycle('ABCD')
>>> new_cycle, backup = itertools.tee(cycle)
>>> type(cycle), type(new_cycle), type(backup)
(itertools.cycle, itertools._tee, itertools._tee)
>>> isinstance(cycle, itertools.cycle), isinstance(cycle, types.GeneratorType)
(True, False)
>>> isinstance(new_cycle, itertools.cycle), isinstance(backup, types.GeneratorType)
(False, False)

So the second time around the generator/cycle is not compared in the correct way. I did have a special case for a cycle but found it more elegant to just treat them all the same way. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add isinstance(value, collections.Iterable) to the first check but that would also catch lists etc which we don't want :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did have a special case for a cycle but found it more elegant to just treat them all the same way.

This might actually be a better way as it ensures we don't change anything at all (even the type of the attributes). I've also realised something else that's not right in this test. Going to tweak and push.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In c5e95d6 I've added a third equality test (theoretically things could break on the second one). I've also added test that ensure the type of a generator/cycle stays the same (which implies I've changed the way I've dealt with the output of itertools.tee). 👍 Let me know what you think :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanations!

p1 = axelrod.Cooperator()
p2 = axelrod.Cooperator()
self.assertEqual(p1, p2)
p1.__repr__ = lambda :"John Nash"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing

- Add equality method and tests to player.
- Add documentation for player equality.
- Refactored some other tests to make use of this method
- Refactor a match generator test which was failing as a result of the
eq method. Previously this was testing equality of instance.

Some other minor changes I've made as a result of this:

- Change when qlearner makes random choice. I've also removed the reset
  test. This is now being tested by the parent test class for all
  strategies.
- Fix small bug in contrite. Without this, contrite was not resetting
  correctly (the recorded history was being maintained in the class
  attribute).

This closes #885 but also closes #940 which was worked on a while ago
but which has not seen any progress.
@marcharper
Copy link
Member

marcharper commented Apr 18, 2017

I approve. One comment for thought -- it seems possible to get into an infinite loop if player A contains an attribute referencing player B and player B contains a reverse reference. I couldn't think of a legitimate reason to do so -- even with the meta players we don't let them be team members of other meta players -- but maybe worth some thought for the possibility of other cases.

@drvinceknight
Copy link
Member Author

That's a really good point. Let me have a play with a couple of ideas I've got. Will push/comment later today.

Check equality of players when players have attributes that point at
each other.

Also included some more tests for basic attribute equality.
@drvinceknight
Copy link
Member Author

I've covered this with: eb612f5

Have added a bunch of tests that check we get what we want :)

(I also added a couple of other specific tests for some cases I thought were worth checking :))

@drvinceknight
Copy link
Member Author

Just a small typo on the word equality ;)

Thanks 👍 :)

@marcharper
Copy link
Member

@meatballs you're up when you get a chance

@meatballs meatballs merged commit 12efc34 into master Apr 19, 2017
@meatballs meatballs deleted the 885 branch April 19, 2017 09:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement an __eq__ method for players

5 participants