The following can be done by step by step, somewhat clumsy way, but I wonder if there are elegant method to do it.
There is a page: http://www.mariowiki.com/Mario_Kart_Wii, where there are 2 tables… there is
Mario - 6 2 2 3 - -
Luigi 2 6 - - - - -
Diddy Kong - - 3 - 3 - 5
[...]
The name “Mario”, etc are the Mario Kart Wii character names.
The numbers are for bonus points for:
Speed Weight Acceleration Handling Drift Off-Road Mini-Turbo
and then there is table 2
Standard Bike S 39 21 51 51 54 43 48
Bullet Bike 53 24 32 35 67 29 67
Bubble Bike / Jet Bubble 48 27 40 40 45 35 37
[...]
These are also the characteristics for the Bike or Kart.
I wonder what’s the most elegant solution for finding all the maximum combinations of Speed, Weight, Acceleration, etc, and also for the minimum, either by directly using the HTML on that page or copy and pasting the numbers into a text file.
Actually, in that character table, Mario to Bower Jr are all medium characters, Baby Mario to Dry Bones are small characters, and the rest are all big characters, except the small, medium, or large Mii are just as what the name says. Small characters can only ride small bike or small kart, and so forth for medium and large.
Update: it would be nice to also filter out only Kart type or Bike when printing the results, as some people only play with bikes, and only for the “in” or “out” drift type for the bikes, as some people only play using “in” drift type. Also nice is to print out result that have the same max or min value (different characters on different kart or bike that add up to the same value). Hugh’s answer of printing out top values and bottom values is nice too as some of values are quite close, such as 73 or 72.
There are two parts to your problem. Parsing the HTML, and doing the analysis. Parsing the HTML is a tedious job and I suspect there’s not a whole lot that you can do to make it elegant. The analysis is not hard, but there are some ways to do this in Python that I think might be considered elegant. I’m going to discuss ways to express the brute force analysis in an elegant and concise fashion. I’m not going to discuss ways to do it faster than brute force because the data set is so tiny.
First up we make a named tuple called “Stats”. This is the object we will use to represent the stats of a driver or vehicle. Named tuples are nice because:
driver.weight__str__format:"Stats(speed=73, weight=56, acceleration=21, handling=17, drift=27, offroad=19, turbo=16)"Let’s define a function to add two sets of stats:
Zip takes two sequences and returns a sequence of pairs of items from each sequence. E.g. zip([1,2,3], [‘a’,’b’,’c’]) == [(1,’a’),(2,’b’),(3,’c’)]). Then we use a list comprehension (
[blah for blah in blah]) to add together the stats from each of these pairs. Finally we feed that to the Stats constructor. The * means that we want to use each item in the sequence as an argument to the function.Now we have defined classes to represent drivers, vehicles and combinations of the two. Note that the constructors for Driver and Vehicle (inherited from ThingWithStats) can take any sequence of the appropriate length as their
statsargument. They use a*to convert the sequence into aStatsobject. We’ll see why this is handy shortly.This function uses a list comprehension to find all combinations of some list of drivers and some list of vehicles. Note that by using multiple “for”s in a single comprehension we get all combinations. This is also sometimes called a Cartesian product.
Now here’s the tediuous bit – the data. I copied and pasted these and used some vim magic to massage them into the correct format. Sorry I don’t have anything more clever for this. Note that for the
statsargument we pass a regular tuple. As mentioned above, the constructor converts with into aStatsobject. This saves us a little clutter here.With that out of the way, we make lists of all the combinations:
Finally we do some basic analysis on the list of all combinations:
The
minandmaxfunctions provide akeyargument for exactly this purpose. It takes a function that takes an item from the list and returns the value that you want to sort by. Here are the results:Further ideas
If you do want to apply this to much larger data sets, you could find the driver and vehicle with the min and max for each stat, and then for each stat combine the max driver with the max vehicle and the min driver with the min vehicle. That would be O(M log M + N log N) instead of O(M*N log M*N). But you would really need to be getting up to thousands of drivers and vehicles before I think this would be an issue.
If you want to apply this to massive data sets that won’t even fit in memory, you could use generator expressions instead of list comprehensions. You would need to combine this with a parser that can read and yield one driver/vehicle at a time.
You could do more specific searches by adding constraints. For example, to find the fastest combination with turbo >= 50 and handling >= 40:
If you want to get all those tied for top place, you could do something like this:
Call it the same as you would call
max. It returns a list of all those items tied for maximum value of whatever key specifies.