Re: [Tutor] Long post: Request comments on starting code and test code on chess rating project.

2017-08-13 Thread boB Stepp
It is rather late here, so I won't get to the links until much later
today, but ...

On Sun, Aug 13, 2017 at 1:42 AM, Steven D'Aprano  wrote:
> I haven't had a chance to read the entire post in detail, but one thing
> which stands out:
>
> On Sun, Aug 13, 2017 at 12:22:52AM -0500, boB Stepp wrote:
>
>> I have started my coding with a RatingCalculator class.  The intent of
>> this class is to gather all methods together needed to validate and
>> calculate chess ratings.  My intent is to only ever have a single
>> instance of this class existing during a given running of the program.
>> (BTW, is there a technique to _guarantee_ that this is always true?)
>
> Yes, this is called the "Singleton" design pattern, which is very
> possibly the most popular and common design pattern in OOP.
>
> It is also very possibly the *least appropriate* use of OOP techniques
> imaginable, so much so that many people consider it to be an
> anti-pattern to be avoided:

[...]

> In Python, the equivalent to the singleton is the module. If you have a
> class full of methods which is only ever going to be instantiated once,
> *almost always* the solution in Python is to use a module full of
> functions.

Hmm.  I should have gone with my gut instinct.  I did not see any
value in a one-object class, and felt I should just have a module with
these functions.  But the goal of this project is to use OOP, so my
intended design was to *force* these methods into a class and use the
class as a convenient container.  OTOH, my other classes I plan to
code -- Player, Game and possibly Database -- make more sense as
classes as the first two will have multiple instances with different
states per object.  And eventually there will be a GUI in tkinter
which is naturally organized by classes.

Thanks, Steve.  This is especially useful feedback as it enhances my
understanding of when and how to implement classes vs. collections of
functions.

boB
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Long post: Request comments on starting code and test code on chess rating project.

2017-08-13 Thread Alan Gauld via Tutor
On 13/08/17 06:22, boB Stepp wrote:

> The intent of this project is more than just calculate chess ratings.
> I also envision being able to store a record of all game results and
> player information for my school chess players that I give lessons to.

That's fair enough but OOP or no OOP the basic advice is the same:
divide and conquer. Make the admin and logic parts of the application
separate and work on the core of each. Bring them together via the
UI (which initially may be a set of CLI tools...).

> I have started my coding with a RatingCalculator class.  The intent of
> this class is to gather all methods together needed to validate and
> calculate chess ratings.  

That may be appropriate in that it is often the case that you have a
single class representing the applications a whole. But a calculator
sounds like a low level helper class to me, possibly encapsulating the
detailed algorithms used top create the ratings. I'd expect there to be
potentially several subclasses representing different algorithms. I'd
expect the calculator instances to be passed as an object into the
rate() method of a game object...

> My intent is to only ever have a single
> instance of this class existing during a given running of the program.

I notice Steven's post, to which I'll add amen.
Singletons in Python are rarely needed, just make your game
a module. Maybe rethink the role of the calculator class.

> added any methods yet.  Currently I am adding class constants that
> will be used in this class' methods. 

Since internal data/attributes should support the operations
its usual to start with the operations before defining
the supporting data. Seriously consider using the CRC
methodology to identify and describe your projects classes.
It can be in a single document rather than physical cards,
the important thing is to briefly describe what each class
is called, its responsibilities(often becoming methods)
and collaborators (other classes which become attributes
or method parameters).

>  These constants are for internal
> class use only and should not be altered from outside the class.  

It sounds like they are to be shared values across
methods of various objects/methods, that suggests
putting them in a shared class or module.

> is the current state of the main.py program (Which will probably be
> broken into multiple modules as the project develops.):

While Python doesn't require using a module per class, it is better to
keep related classes in a single module so I strongly suggest breaking
it into multiple modules. At the least 3: rating engine, admin and UI.

> """Module to process chess ratings."""
> 
> class RatingCalculator:
> """This class contains methods to validate and calculate chess ratings."""

I'd suggest that validation and calculation are two very different
things. Probably requiring separate classes. But we need more detail
on the objects involved. What does a rating rate - a Game? a Move? a
strategy? And what does validation validate? A rating perhaps?
You are in danger of focusing on the verbs (rating calculate,
create, edit, etc rather than the objects that do these things.
You need to turn your initial thoughts away from what the application
does (traditional procedural coding style) and onto thinking
about what kind of objects make up the application. It's common
to start with a fairly long list then, as you apply CRC analysis(*),
realize that many of the objects are only attributes. But it's better
to start with too many objects than to try to squeeze functionality
into just a few uber-objects.


(*)If you discover that a candidate class has no responsibility
except holding one or maybe two pieces of information, or that
it only collaborates with one other object you may decide it
only needs be an attribute not a full class. Also, especially
in Python, many collections of classes will turn out to be
standard container types such as lists, dicts, tuples etc.
Or standard classes such as dates/times. Making these early
win decisions is one of the biggest reasons for doing CRC
analysis IMHO.

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Long post: Request comments on starting code and test code on chess rating project.

2017-08-13 Thread Peter Otten
boB Stepp wrote:

> I mentioned in one of the recent threads that I started that it is
> probably time for me to attempt a substantial project using the OO
> paradigm.  I have had no coursework or training in OOP other than my
> recent self-studies.  Everything (Other than toy practice examples.) I
> have coded to date has been strictly procedural, similar to what I
> learned in FORTRAN ages ago.  Also, I am continuing to try to do TDD
> and keep things DRY.  So I am hoping for a thorough critique of what I
> will shortly present.  Everything is fair game!  Hopefully my
> self-esteem is up to this critique!
> 
> The intent of this project is more than just calculate chess ratings.
> I also envision being able to store a record of all game results and
> player information for my school chess players that I give lessons to.
> Some functionality I will strive to include:  1) Be able to re-rate
> all games from a given date to the present.  2) Record demographic
> information about my players, such as names, current school year,
> current grade, etc.  3) Have a very friendly GUI so that when I have a
> finished project, a student could easily take over responsibility of
> maintaining the school's player ratings, if I so choose to allow this.
> 4) Having an editor where games and player data (But NOT ratings;
> these would have to be re-rated starting from a corrected game(s).)
> can be edited, inserted, or deleted with games (If game data is
> edited.) automatically be re-rated.  5) Et cetera ...
> 
> My current project structure is:
> 
> /Projects
> /rating_calculator
> /rating_calculator
> __init__.py
> main.py
> /tests
> __init__.py
> test_main.py
> 
> I have started my coding with a RatingCalculator class.  The intent of
> this class is to gather all methods together needed to validate and
> calculate chess ratings.  My intent is to only ever have a single
> instance of this class existing during a given running of the program.

Why?

> (BTW, is there a technique to _guarantee_ that this is always true?)

Why? Would it harm one instance if there were another one? If so you have 
global state which usually runs counter to the idea of a class.

> I am only at the very beginning of coding this class.  I have not
> added any methods yet.  Currently I am adding class constants that
> will be used in this class' methods.  These constants are for internal
> class use only and should not be altered from outside the class.  

Still, a good unit test might be to initialise a RatingCalcultator with 
different "highest possible ratings" and then verify for each instance that 
actual ratings never exceed the specified value.

> Here
> is the current state of the main.py program (Which will probably be
> broken into multiple modules as the project develops.):
> 
> 
=
> #!/usr/bin/env python3
> 
> """Module to process chess ratings."""
> 
> class RatingCalculator:
> """This class contains methods to validate and calculate chess
> ratings."""
> 
> # Overestimated extremes for possible chess rating values.  Values
> # outside this range should not be achievable.

If there are no hard limits, why give a limit at all?

> 
> _LOWEST_POSSIBLE_RATING = 0

Again, this is just a number. Is it really worthwile explicit checking in a 
unit test? The most relevant tests address behaviour.

> _HIGHEST_POSSIBLE_RATING = 3000
> 
> # Fundamental constants for use in the chess rating formula.  The keys
> # to these values are tuples giving the rating ranges for which these
> # K-factors are valid.
> 
> _K_MULTIPLIERS = {
> (_LOWEST_POSSIBLE_RATING, 2099): 0.04,

The Python way uses half-open intervals. This has the advantage that there 
is a well-defined k-multiplier for a rating of 2099.5. Note that if you 
apply that modification you no longer need any upper limits. Use bisect (for 
only threee values linear search is OK, too) to find the interval from a 
list like [0, 2100, 2400], then look up the multiplier in a second list 
[0.04, 0.03, 0.02])

> (2100, 2399): 0.03,
> (2400, _HIGHEST_POSSIBLE_RATING): 0.02}
> 
> _K_ADDERS = {
> (_LOWEST_POSSIBLE_RATING, 2099): 16,
> (2100, 2399): 12,
> (2400, _HIGHEST_POSSIBLE_RATING): 8}
> 
=
> 
> The test code in test_main.py is:
> 
> 
=
> #!/usr/bin/env python3
> 
> """Module to test all functions and methods of main.py."""
> 
> import unittest
> import rating_calculator.main as main
> 
> class TestRatingCalculatorConstants(unittest.TestCase):
> # Check that the constants in RatingCalculator have the proper values.
> 
> def setUp(self):
> # Create instance of Ratin

Re: [Tutor] Long post: Request comments on starting code and test code on chess rating project.

2017-08-13 Thread boB Stepp
Based upon the feedback thus far (Many thanks!) I think that perhaps I
should expand on my design thoughts as I have already stubbed my big
toe at the very start!

On Sun, Aug 13, 2017 at 12:22 AM, boB Stepp  wrote:

> The intent of this project is more than just calculate chess ratings.

CALCULATION OF RATINGS

My calculation of a new chess rating is a simpler process than what is
used for FIDE Elo ratings or USCF Elo ratings for over-the-board play.
Instead, I am adapting the USCF (United States Chess Federation)
correspondence chess rating formula for use with my students.  These
students usually come to me often not even knowing the moves and rules
of the game.  They have no previous tournament chess experience and in
general are quite weak players even if they sometimes believe
otherwise. ~(:>))  A link to the USCF's explanation of this rating
system is:  http://www.uschess.org/content/view/7520/393/

It is not very well-written in my opinion.  But anyway ... The basic formula is:

new_rating = old_rating + K_MULTIPLIER * (opponent_rating -
old_rating) + K_ADDER * (your_result - opponent_result)

If you win your_result = 1; lose = 0; draw = 0.5.  Note that the
opponent's result would be, respectively, 0, 1 and 0.5, corresponding
to these values of your_result.

I am not using the article's section on unrated players.  This is of
little use to me as I usually start off without anyone who has ever
had a chess rating.  So there is no existing rating pool to
meaningfully compare my unrated players against.  So I start out each
unrated player with a rating of 1000.  For most of my students who
already know and play chess, this turns out to be a slightly generous
estimate of their rating if I were to compare them to the USCF
over-the-board rating population.

Another change from the article I am making is that if there is more
than a 350 rating point difference between players, then the game is
rated as if it is a 350 point difference.  I have changed this to 400
points as I feel that the higher rated player should _not_ gain any
rating points for beating the lower rated player.  In practice I am
the only person 400+ points greater than anyone.  To date I have a
100% win rate against my students, so I would rather they not obsess
about losing rating points against their peers if I have to fill in
for a game due to an odd number of players showing up.

There are several conditionals that have to be applied to calculate
the rating even in this simplified system.  K_MULTIPLIER and K_ADDER
are dependent on whether the player's old_rating falls into one of
these rating ranges:  1)  0 - 2099; 2) 2100 - 2399; 3) >= 2400.
Additionally, if the initial new_rating calculation causes new_rating
to fall into a different K-factor rating range, then the new rating
must be proportionately adjusted for the number of points that fall
into the new K-factor rating range, using a ratio of the new and old
K-factors.

So based on what Steve said, the rating calculation makes more sense
to be done with functions.  One of the things I wish to do with the
completed program, is to enter all of my old game data spanning ~ 5
years, and have the program re-rate everything from scratch.  So I do
not see how it would be helpful to create a new RatingCalculator
instance for each game.

PERSISTENT STORAGE OF GAME DATA AND PLAYER DATA

I've decided not to use an actual database as I will never have so
much data that it cannot be fully loaded into RAM for even quite
modest PCs.  The approach I am thinking of taking is using a
dictionary of dictionaries to store the game data and player data.
Something like:

players = {0: {'last name': 'Smith', 'first name': 'John', ... },
   1: {'last name': 'Doe', 'first name': 'Doe', ... },
   ... ,
   n: {...}}

and similarly for games.  I would use the csv library's dictread and
dictwrite methods to load into memory and on program exit write back
to disk these two dictionaries, which are meant to mimic two database
tables.

REPORT GENERATION

There are a variety of reports that I would like to be able to print
to screen or paper.  Things such as a "Top x List" of rated players,
full rating list sorted from highest rating to lowest, rating lists
for the current school year only or a particular past school year, and
so.  I could see wanting to sort such lists by various criteria
depending on the type of report I would like to generate.  Currently
this section of the program is a big gray area for me, but I see no
reason why my two dictionaries cannot be manipulated to pull the
desired results for each imagined type of report.

USER INTERFACE

Until all of the above is worked out, I will use a CLI, perhaps even
play around with the curses library, which I have been meaning to do.
Once I get an overall result that I am happy with, I will develop a
GUI in tkinter.  My intent is to go as Alan suggests and keep the UI
well-decoupled from the rest of the program, no matter if it is CLI or

[Tutor] "Path tree"

2017-08-13 Thread Michael C
Hi all:

I am trying to formulate a "path-finding" function, and I am stuck on this
problem:

Please look at the picture attached: Those dots are coordinates of (x,y),
and this tree can be thought of as a list of tuples, with each tuple
consisting of (x,y).  Now I am trying to make a function go through this
list of tuples and then return the "path." to go from, say, 4 to 8. If I
simply compute for the dot for shortest distance, then the solution would
be to go from 4 to 8 direct, but that doesn't work, because the correct
solution should have been 4,3,2,5,6,8.


How do I do this?

Thanks!
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Long post: Request comments on starting code and test code on chess rating project.

2017-08-13 Thread Alan Gauld via Tutor
On 13/08/17 21:15, boB Stepp wrote:

I return to the point I made about focusing on the
objects not the functionality.

> It is not very well-written in my opinion.  But anyway ... The basic formula 
> is:
> 
> new_rating = old_rating + K_MULTIPLIER * (opponent_rating -
> old_rating) + K_ADDER * (your_result - opponent_result)

What is being rated? A player? A game? a set of games?
The rating calculation should probably be a method of
the object being rated rather than a standalone function.

If you make it a function you will probably have to pass
in lots of data objects (or worse use a lot of globals).
In OOP you want the data to be in the object and the
method to use that data to achieve its aim.

> I generally start as above by dividing things up into broad areas of
> functionality 

Which is exactly how you write procedural code - the technique
is called functional decomposition.

But if your objective is to use OOP you should start by identifying
the objects. Then you can assign the functionality(responsibilities)
to which ever object (or set of objects) is appropriate.


> to be a rather straightforward translation of a formula with various
> conditions into code, returning the newly calculated ratings 

Note that I'm not saying your approach is wrong in a general case,
it may well be the most appropriate approach for this application.
But it will not easily lead to an OOP style solution, and that I
thought was the secondary(or even primary?) objective of this
exercise?

> So how detailed should I plan out each broad section of the program
> before writing any code?  Alan seems to be suggesting getting a firm
> handle on initially imagined objects and their methods before any code
> writing.

You need some kind of idea of the classes you are going to write.
The CRC description only needs to be a few lines scribbled on a
sheet of paper or in a text editor. In OOP you are building classes that
represent instances. Your solution is an interaction between the
instances (objects). Without a fairly clear idea of how the objects
interact you can't make a sensible start. You don't need to analyse
the whole system (and indeed should not do so) but start with a
couple of basic use cases - what initiates the action? What are
the  preconditions(eg. data etc), the outcomes, the possible error
conditions. When you understand a use case enough to code it, do so.
Or even just the initial class/object.

It's a good idea to get an early code structure in place, maybe
a Player class? It seems that this is the thing being rated.
Maybe a game class since it seems that game (results and players)
are used in the rating algorithm. So your Player probably needs
a list of past games?

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Long post: Request comments on starting code and test code on chess rating project.

2017-08-13 Thread boB Stepp
On Sun, Aug 13, 2017 at 3:52 AM, Peter Otten <__pete...@web.de> wrote:
> boB Stepp wrote:

>> I am only at the very beginning of coding this class.  I have not
>> added any methods yet.  Currently I am adding class constants that
>> will be used in this class' methods.  These constants are for internal
>> class use only and should not be altered from outside the class.
>
> Still, a good unit test might be to initialise a RatingCalcultator with
> different "highest possible ratings" and then verify for each instance that
> actual ratings never exceed the specified value.

I just had not gotten around to doing this yet.

> =
>> #!/usr/bin/env python3
>>
>> """Module to process chess ratings."""
>>
>> class RatingCalculator:
>> """This class contains methods to validate and calculate chess
>> ratings."""
>>
>> # Overestimated extremes for possible chess rating values.  Values
>> # outside this range should not be achievable.
>
> If there are no hard limits, why give a limit at all?

My thinking was to put in a sanity check.  The ratings should fall
into sensible limits.

>>
>> _LOWEST_POSSIBLE_RATING = 0
>
> Again, this is just a number. Is it really worthwile explicit checking in a
> unit test? The most relevant tests address behaviour.

Shouldn't I check for inadvertent changing of what are constants by
later modifications of the code?

>> _HIGHEST_POSSIBLE_RATING = 3000
>>
>> # Fundamental constants for use in the chess rating formula.  The keys
>> # to these values are tuples giving the rating ranges for which these
>> # K-factors are valid.
>>
>> _K_MULTIPLIERS = {
>> (_LOWEST_POSSIBLE_RATING, 2099): 0.04,
>
> The Python way uses half-open intervals. This has the advantage that there
> is a well-defined k-multiplier for a rating of 2099.5. Note that if you
> apply that modification you no longer need any upper limits. Use bisect (for
> only threee values linear search is OK, too) to find the interval from a
> list like [0, 2100, 2400], then look up the multiplier in a second list
> [0.04, 0.03, 0.02])

Note:  Valid ratings can only be positive integers.  No floats allowed
in the final rating result or in the initial input of an established
rating that is to be changed.

So you are suggesting doing something like:

---
py3: from bisect import bisect
py3: def get_k_factors(rating):
... RATING_BREAKPOINTS = [0, 2100, 2400]
... K_ADDERS = [16, 12, 8]
... K_MULTIPLIERS = [0.04, 0.03, 0.02]
... index = bisect(RATING_BREAKPOINTS, rating) - 1
... k_adder = K_ADDERS[index]
... k_multiplier = K_MULTIPLIERS[index]
... return k_adder, k_multiplier
...
py3: get_k_factors(1900)
(16, 0.04)
py3: get_k_factors(2100)
(12, 0.03)
py3: get_k_factors(2399)
(12, 0.03)
py3: get_k_factors(2400)
(8, 0.02)
---

I like it!  It will save me from having to use a bunch of conditionals.


>> (2100, 2399): 0.03,
>> (2400, _HIGHEST_POSSIBLE_RATING): 0.02}
>>
>> _K_ADDERS = {
>> (_LOWEST_POSSIBLE_RATING, 2099): 16,
>> (2100, 2399): 12,
>> (2400, _HIGHEST_POSSIBLE_RATING): 8}
>>
> =
>>
>> The test code in test_main.py is:
>>
>>
> =
>> #!/usr/bin/env python3
>>
>> """Module to test all functions and methods of main.py."""
>>
>> import unittest
>> import rating_calculator.main as main
>>
>> class TestRatingCalculatorConstants(unittest.TestCase):
>> # Check that the constants in RatingCalculator have the proper values.
>>
>> def setUp(self):
>> # Create instance of RatingCalculator for use in the following
>> # tests. Create tuple of test-value pairs to interate over in
>> # subtests.
>>
>> self.rating_calculator = main.RatingCalculator()
>> self.test_value_pairs = (
>> (self.rating_calculator._LOWEST_POSSIBLE_RATING, 0),
>
> With the _ you are making these constants implementation details. Isn't one
> goal of unit tests to *allow* for changes in the implementation while
> keeping the public interface?

I don't think I am understanding your thoughts.  Would you please
provide an illustrative example?  I am concerned that later program
development or maintenance might inadvertently alter values that are
expected to be constants.

>> (self.rating_calculator._HIGHEST_POSSIBLE_RATING, 3000),
>> (self.rating_calculator._K_MULTIPLIERS[(
>> self.rating_calculator._LOWEST_POSSIBLE_RATING,
>> 2099)], 0.04),
>> (self.rating_calculator._K_MULTIPLIERS[(2100, 2399)],
>>   

Re: [Tutor] Long post: Request comments on starting code and test code on chess rating project.

2017-08-13 Thread boB Stepp
On Sun, Aug 13, 2017 at 5:49 PM, Alan Gauld via Tutor  wrote:
> On 13/08/17 21:15, boB Stepp wrote:
>
> I return to the point I made about focusing on the
> objects not the functionality.
>
>> It is not very well-written in my opinion.  But anyway ... The basic formula 
>> is:
>>
>> new_rating = old_rating + K_MULTIPLIER * (opponent_rating -
>> old_rating) + K_ADDER * (your_result - opponent_result)
>
> What is being rated? A player? A game? a set of games?
> The rating calculation should probably be a method of
> the object being rated rather than a standalone function.

The result of a game between two players results in the need to
calculate new ratings for both players.  Ratings are attributes of
players.

>> I generally start as above by dividing things up into broad areas of
>> functionality
>
> Which is exactly how you write procedural code - the technique
> is called functional decomposition.

Got me there!  Still stuck in the old procedural paradigm.

> But if your objective is to use OOP you should start by identifying
> the objects. Then you can assign the functionality(responsibilities)
> to which ever object (or set of objects) is appropriate.
>
>
>> to be a rather straightforward translation of a formula with various
>> conditions into code, returning the newly calculated ratings
>
> Note that I'm not saying your approach is wrong in a general case,
> it may well be the most appropriate approach for this application.
> But it will not easily lead to an OOP style solution, and that I
> thought was the secondary(or even primary?) objective of this
> exercise?

You understand correctly!  The primary purpose of this project is
breakout of procedural programming and break into OOP.

>> So how detailed should I plan out each broad section of the program
>> before writing any code?  Alan seems to be suggesting getting a firm
>> handle on initially imagined objects and their methods before any code
>> writing.
>
> You need some kind of idea of the classes you are going to write.
> The CRC description only needs to be a few lines scribbled on a
> sheet of paper or in a text editor. In OOP you are building classes that
> represent instances. Your solution is an interaction between the
> instances (objects). Without a fairly clear idea of how the objects
> interact you can't make a sensible start. You don't need to analyse
> the whole system (and indeed should not do so) but start with a
> couple of basic use cases - what initiates the action? What are
> the  preconditions(eg. data etc), the outcomes, the possible error
> conditions. When you understand a use case enough to code it, do so.
> Or even just the initial class/object.

I just finished reading a couple of online articles about CRC cards.
I have some old index cards lying around.  I will have at it.

> It's a good idea to get an early code structure in place, maybe
> a Player class? It seems that this is the thing being rated.
> Maybe a game class since it seems that game (results and players)
> are used in the rating algorithm. So your Player probably needs
> a list of past games?

The two most obvious classes that jumped to my mind from the get-go
are Player and Game.

Thanks, Alan!

-- 
boB
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor