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

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

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

>> 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.

>> 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?

In what scenario would such an inadvertent change occur?

I prefer the indirect check sketched out above. Assuming there is a 
check_rating() method you can make tests like

rc = RatingCalculator(lowest_rating=0, highest_rating=100)
for r in [0, 50, 100]:
rc.check_rating(r)
for r in [-1, 101]:
with self.assertRaises(FailedSanityCheck):
rc.check_rating(101)

that ensure that a method works as expected rather than verifying a certain 
constant value. (You have to do the above for at least two ranges to make 
sure that the arguments are used for the check.)


> 
>>> _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.

Then you need a test for that ;)
 
> So you are suggesting doing something like:

> py3: from bisect import bisect
> py3: def get_k_factors(rating):

Yes, but I would pass the lists as arguments to allow for easier testing.

> ... 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
> ...

>> 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?  

When you are sending a parcel to a friend you don't care about the path it 
takes or that the delivery service obeys the speed limit. You only demand 
that it arrives at the specified place within the specified time interval.
That way the service is free to choose the best (quickest, shortest, or 
cheapest) route and the best (fastest, cheapest) means of transportation.

A class is a service provider, too. If you test private methods and states 
you are either stuck with one implementation or have to change unit tests 
and implementation in lockstep -- and these unit test modifications may 
introduce bugs, too.

If instead you manage to cover the code with tests that only use the public 
interface you can replace one implementation of the tested class with a 
completely different one -- and when your unit tests continue to succeed you 
are pretty sure that the replacement is correct, too. 

Thus the hurdle to trying out improvements ("refactoring") is lower and you 
are more likely to end up with clean and efficient code.

> I am concerned that later program
> development or maintenance might inadvertently alter values that are
> expected to be constants.

Unlike 0 the value of 3000 is in no way special. Your script will be more 
robust if it continues to work with an upper bound of 2900 or 4000 -- or 
even floats in the range 0 <= rating < 1.

>> Values are the most volatile and least important part of a program.
>> Start coding with the algorithms and tests covering those.
> 
> I was just looking for the lowest hanging fruit to get an easy start
> on this project, and use it as a check to see if I was going off in
> inadvisable directions.

I think that is mostly a distraction. 

Unfortunately there are many ways to burn time dealing with the irrelevant 
aspects of a program. I know some of them -- from hearsay, of course ;)

___

Re: [Tutor] "Path tree"

2017-08-15 Thread Peter Otten
Martin A. Brown wrote:

> The image:
> 
>> http://imgur.com/a/CwA2G
> 
> To me, this looks like a 'graph', which is a more general data
> structure -- it does not look like a 'tree' (in the computer-science
> meaning of the term, anyway).

> import networkx as nx

While Martin's solution is certainly more robust it may also be instructive 
to see it done "by hand":

edges = [
(1, 2),
(2, 5),
(2, 3),
(3, 4),
(5, 7),
(5, 6),
(6, 8),
# remove the comment to see what happens 
# when there is more than one path between two nodes
#(1, 8), 
]

graph = {}

# make a lookup table node --> neighbours
for a, b in edges:
graph.setdefault(a, []).append(b)
graph.setdefault(b, []).append(a)
print(graph)

def connect(start, end, path, graph):
path += (start,)
if start == end:
# we found a connection
yield path
neighbours = graph[start]

# try all neighbours, but avoid cycles to nodes already in the path
for node in neighbours:
if node not in path:
# recurse to find connection from neigbour to end
yield from connect(node, end, path, graph)

for path in connect(4, 8, (), graph):
print(path)

___
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-15 Thread Neil Cerutti
On 2017-08-13, boB Stepp  wrote:
> 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.

You really can do it, but to me this requirement argues for going
back to a database backend. Why rewrite SQL?

-- 
Neil Cerutti

___
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-15 Thread Alan Gauld via Tutor
On 15/08/17 15:09, Neil Cerutti wrote:

>> 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.  ...

> You really can do it, but to me this requirement argues for going
> back to a database backend. Why rewrite SQL?

I agree with Neil, this is exactly what SQL is good for and
would make this part of the project much easier and would have
the added benefit of introducing you to one of the trickiest,
but most common, bits of OOP - object persistence...

You might even find yourself writing some class methods! ;-)

And using SQLite, (even using in-memory mode if you don't
want to keep a database file) is about as easy as SQL gets.

If you do want to do it by hand consider using the itertools
module. (eg. its filterfalse(), dropwhile()/takewhile()
combinations() and groupby() functions.)

-- 
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] "Path tree"

2017-08-15 Thread Cameron Simpson

On 14Aug2017 12:10, Michael C  wrote:

http://imgur.com/a/CwA2G


Ok. So you have a graph like this:

 1 -- 2 -- 3 -- 4
  |
 7 -- 5 -- 6 -- 8

Have a read of a graph theory textbook. Also, wikipedia has an article on 
finding the shortest path through a graph:


 https://en.wikipedia.org/wiki/Shortest_path_problem

which references several algorithms.

You could pick one (eg Dijkstra's algorithm) and try to implement it. For a 
graph this small you could try your own and do something rather brute force; in 
larger graphs efficiency becomes very important.


You will need to express the graph as a data structure in your code, with a 
data structure that expresses which nodes connect to each other node. Typically 
this often includes weights for the edges and a direction, but your graph has 
no weights (cost of traversing a particular edge) and is undirected (you can 
traverse an edge in either direction). It is also "simply connected" - there 
are no loops. All these things make your task simpler.


You can express a graph as a direcionary with keys being your node numbers 
(i.e. 1, 2, 3 etc) and the values being a list of the other nodes to which each 
connects. Eg:


 graph = {
   1: [2],
   2: [1, 3],
   3: [2, 4],
   4: [3],
   5: [7, 6],
   6: [5, 8],
   7: [5],
   8: [6]
 }

The you need to write code that starts somewhere (4 in your example) and moves 
to other nodes until it reaches the target node (8 in your example). You can 
see which other nodes are reachable your current from the dictionary above. You 
need to keep some kind of record of which nodes you have visted (i.e. that is 
the path).


See if that gets you started. For debugging, make your program print out what 
node it is at as it traverses the graph - that will be helpful to you in 
figuring out what is working and what is not.


Cheers,
Cameron Simpson  (formerly c...@zip.com.au)
___
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-15 Thread boB Stepp
On Tue, Aug 15, 2017 at 12:29 PM, Alan Gauld via Tutor  wrote:
> On 15/08/17 15:09, Neil Cerutti wrote:
>
>>> 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.  ...
>
>> You really can do it, but to me this requirement argues for going
>> back to a database backend. Why rewrite SQL?

I just thought this would be an interesting problem type to tackle.
No intentions of reinventing SQL!

> I agree with Neil, this is exactly what SQL is good for and
> would make this part of the project much easier and would have
> the added benefit of introducing you to one of the trickiest,
> but most common, bits of OOP - object persistence...

Would you expand on this in SQLite terms?

> You might even find yourself writing some class methods! ;-)

Nah.  I might expand my array of constants, and test a few more ... ~(:>))



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