mega-commit, some from now, some from months ago. added a lot of logging tonight so we can watch a game in real-time. also included slowdown for the same purpose

This commit is contained in:
Zev B Averbach
2020-11-25 19:55:13 +01:00
parent 40e94c0c43
commit c3ed6d0614
32 changed files with 2976 additions and 78 deletions

BIN
.coverage Normal file

Binary file not shown.

5
.coveragerc Normal file
View File

@@ -0,0 +1,5 @@
[run]
omit =
*/site-packages/*
*/distutils/*
tests/*

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

88
buy_decision_algos.py Normal file
View File

@@ -0,0 +1,88 @@
"""
from
[here](https://blog.ed.ted.com/2017/12/01/heres-how-to-win-at-monopoly-according-to-math-experts/):
'For every property (apart from the brown set — which, lets be honest, is basically pointless),
its the third house that is really worth investing in quickly. After that, build more if you have
the money, but its probably worth waiting a few turns if cash is a bit tight. Since there are a
limited number of houses in the game, building three houses on properties early and then waiting
to upgrade further has the added advantage of potentially blocking the building projects of other
players. Sneaky, huh?'
'utilities are completely pointless.'
"""
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from monopoly import Property, Player
class BuyDecision(ABC):
@abstractmethod
def __call__(self, property_: "Property", player: "Player"):
pass
class BuyEverything(BuyDecision):
def __call__(self, _, __):
return True
class BuyIfHaveThreeTimesPrice(BuyDecision):
def __call__(self, property_: "Property", player: "Player"):
return player.money >= property_.cost * 3
class BuyIfDontHaveTwoPartialMonopoliesOfOtherColors(BuyDecision):
def __call__(self, property_: "Property", player: "Player"):
num_partial_monopolies = 0
for property_type, properties in player.properties_by_type.items():
if property_type in ("railroad", "utility"):
continue
if len(properties) == 3:
num_partial_monopolies += 1
if num_partial_monopolies == 3:
print(
f"{player.name} isn't buying {property_} because "
f"they have {num_partial_monopolies} monopolies"
)
return False
return True
class BuyIfOwnFewerThanFivePropertiesOrHaveOneOfThisColor(BuyDecision):
def __call__(self, property_, player):
num_properties = len(player.properties)
if num_properties < 5:
print(f"{player} wants to buy {property_}")
return True
for property_type, properties in player.properties_by_type.items():
if property_type == property_.type:
print(f"{player} wants to buy {property_}")
return True
print(f"{player} doesn't want to buy {property_}")
return False
class BuyIfNoOneOwnsTypeAndIsOfTheOneTypeOwned(BuyDecision):
"""
ALGORITHM
---------
If nobody owns this type of property, buy it.
If only one player owns all owned property of this type, buy it.
If this player owns any of this type fo property, buy it.
"""
def __call__(self, property_, player):
properties_of_this_type = Property.instances_by_type()[property_.type]
if not any(p.owner for p in properties_of_this_type):
return True
# prevent others from getting monopolies
if (
len(set([p.owner for p in Property.instances_by_type()[property_.type]]))
== 1
):
return True
players_property_types = player.properties_by_type.keys()
if player.properties and property_.type in players_property_types:
return True
return False

View File

@@ -10,6 +10,10 @@ class NotEnough(Exception):
pass
class NotEnoughPlayers(NotEnough):
pass
class MustBeEqualAmounts(Exception):
pass

589
htmlcov/coverage_html.js generated Normal file
View File

@@ -0,0 +1,589 @@
// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
// For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
// Coverage.py HTML report browser code.
/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
/*global coverage: true, document, window, $ */
coverage = {};
// Find all the elements with shortkey_* class, and use them to assign a shortcut key.
coverage.assign_shortkeys = function () {
$("*[class*='shortkey_']").each(function (i, e) {
$.each($(e).attr("class").split(" "), function (i, c) {
if (/^shortkey_/.test(c)) {
$(document).bind('keydown', c.substr(9), function () {
$(e).click();
});
}
});
});
};
// Create the events for the help panel.
coverage.wire_up_help_panel = function () {
$("#keyboard_icon").click(function () {
// Show the help panel, and position it so the keyboard icon in the
// panel is in the same place as the keyboard icon in the header.
$(".help_panel").show();
var koff = $("#keyboard_icon").offset();
var poff = $("#panel_icon").position();
$(".help_panel").offset({
top: koff.top-poff.top,
left: koff.left-poff.left
});
});
$("#panel_icon").click(function () {
$(".help_panel").hide();
});
};
// Create the events for the filter box.
coverage.wire_up_filter = function () {
// Cache elements.
var table = $("table.index");
var table_rows = table.find("tbody tr");
var table_row_names = table_rows.find("td.name a");
var no_rows = $("#no_rows");
// Create a duplicate table footer that we can modify with dynamic summed values.
var table_footer = $("table.index tfoot tr");
var table_dynamic_footer = table_footer.clone();
table_dynamic_footer.attr('class', 'total_dynamic hidden');
table_footer.after(table_dynamic_footer);
// Observe filter keyevents.
$("#filter").on("keyup change", $.debounce(150, function (event) {
var filter_value = $(this).val();
if (filter_value === "") {
// Filter box is empty, remove all filtering.
table_rows.removeClass("hidden");
// Show standard footer, hide dynamic footer.
table_footer.removeClass("hidden");
table_dynamic_footer.addClass("hidden");
// Hide placeholder, show table.
if (no_rows.length > 0) {
no_rows.hide();
}
table.show();
}
else {
// Filter table items by value.
var hidden = 0;
var shown = 0;
// Hide / show elements.
$.each(table_row_names, function () {
var element = $(this).parents("tr");
if ($(this).text().indexOf(filter_value) === -1) {
// hide
element.addClass("hidden");
hidden++;
}
else {
// show
element.removeClass("hidden");
shown++;
}
});
// Show placeholder if no rows will be displayed.
if (no_rows.length > 0) {
if (shown === 0) {
// Show placeholder, hide table.
no_rows.show();
table.hide();
}
else {
// Hide placeholder, show table.
no_rows.hide();
table.show();
}
}
// Manage dynamic header:
if (hidden > 0) {
// Calculate new dynamic sum values based on visible rows.
for (var column = 2; column < 20; column++) {
// Calculate summed value.
var cells = table_rows.find('td:nth-child(' + column + ')');
if (!cells.length) {
// No more columns...!
break;
}
var sum = 0, numer = 0, denom = 0;
$.each(cells.filter(':visible'), function () {
var ratio = $(this).data("ratio");
if (ratio) {
var splitted = ratio.split(" ");
numer += parseInt(splitted[0], 10);
denom += parseInt(splitted[1], 10);
}
else {
sum += parseInt(this.innerHTML, 10);
}
});
// Get footer cell element.
var footer_cell = table_dynamic_footer.find('td:nth-child(' + column + ')');
// Set value into dynamic footer cell element.
if (cells[0].innerHTML.indexOf('%') > -1) {
// Percentage columns use the numerator and denominator,
// and adapt to the number of decimal places.
var match = /\.([0-9]+)/.exec(cells[0].innerHTML);
var places = 0;
if (match) {
places = match[1].length;
}
var pct = numer * 100 / denom;
footer_cell.text(pct.toFixed(places) + '%');
}
else {
footer_cell.text(sum);
}
}
// Hide standard footer, show dynamic footer.
table_footer.addClass("hidden");
table_dynamic_footer.removeClass("hidden");
}
else {
// Show standard footer, hide dynamic footer.
table_footer.removeClass("hidden");
table_dynamic_footer.addClass("hidden");
}
}
}));
// Trigger change event on setup, to force filter on page refresh
// (filter value may still be present).
$("#filter").trigger("change");
};
// Loaded on index.html
coverage.index_ready = function ($) {
// Look for a localStorage item containing previous sort settings:
var sort_list = [];
var storage_name = "COVERAGE_INDEX_SORT";
var stored_list = undefined;
try {
stored_list = localStorage.getItem(storage_name);
} catch(err) {}
if (stored_list) {
sort_list = JSON.parse('[[' + stored_list + ']]');
}
// Create a new widget which exists only to save and restore
// the sort order:
$.tablesorter.addWidget({
id: "persistentSort",
// Format is called by the widget before displaying:
format: function (table) {
if (table.config.sortList.length === 0 && sort_list.length > 0) {
// This table hasn't been sorted before - we'll use
// our stored settings:
$(table).trigger('sorton', [sort_list]);
}
else {
// This is not the first load - something has
// already defined sorting so we'll just update
// our stored value to match:
sort_list = table.config.sortList;
}
}
});
// Configure our tablesorter to handle the variable number of
// columns produced depending on report options:
var headers = [];
var col_count = $("table.index > thead > tr > th").length;
headers[0] = { sorter: 'text' };
for (i = 1; i < col_count-1; i++) {
headers[i] = { sorter: 'digit' };
}
headers[col_count-1] = { sorter: 'percent' };
// Enable the table sorter:
$("table.index").tablesorter({
widgets: ['persistentSort'],
headers: headers
});
coverage.assign_shortkeys();
coverage.wire_up_help_panel();
coverage.wire_up_filter();
// Watch for page unload events so we can save the final sort settings:
$(window).unload(function () {
try {
localStorage.setItem(storage_name, sort_list.toString())
} catch(err) {}
});
};
// -- pyfile stuff --
coverage.pyfile_ready = function ($) {
// If we're directed to a particular line number, highlight the line.
var frag = location.hash;
if (frag.length > 2 && frag[1] === 't') {
$(frag).addClass('highlight');
coverage.set_sel(parseInt(frag.substr(2), 10));
}
else {
coverage.set_sel(0);
}
$(document)
.bind('keydown', 'j', coverage.to_next_chunk_nicely)
.bind('keydown', 'k', coverage.to_prev_chunk_nicely)
.bind('keydown', '0', coverage.to_top)
.bind('keydown', '1', coverage.to_first_chunk)
;
$(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");});
$(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");});
$(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");});
$(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");});
coverage.assign_shortkeys();
coverage.wire_up_help_panel();
coverage.init_scroll_markers();
// Rebuild scroll markers when the window height changes.
$(window).resize(coverage.build_scroll_markers);
};
coverage.toggle_lines = function (btn, cls) {
btn = $(btn);
var show = "show_"+cls;
if (btn.hasClass(show)) {
$("#source ." + cls).removeClass(show);
btn.removeClass(show);
}
else {
$("#source ." + cls).addClass(show);
btn.addClass(show);
}
coverage.build_scroll_markers();
};
// Return the nth line div.
coverage.line_elt = function (n) {
return $("#t" + n);
};
// Return the nth line number div.
coverage.num_elt = function (n) {
return $("#n" + n);
};
// Set the selection. b and e are line numbers.
coverage.set_sel = function (b, e) {
// The first line selected.
coverage.sel_begin = b;
// The next line not selected.
coverage.sel_end = (e === undefined) ? b+1 : e;
};
coverage.to_top = function () {
coverage.set_sel(0, 1);
coverage.scroll_window(0);
};
coverage.to_first_chunk = function () {
coverage.set_sel(0, 1);
coverage.to_next_chunk();
};
// Return a string indicating what kind of chunk this line belongs to,
// or null if not a chunk.
coverage.chunk_indicator = function (line_elt) {
var klass = line_elt.attr('class');
if (klass) {
var m = klass.match(/\bshow_\w+\b/);
if (m) {
return m[0];
}
}
return null;
};
coverage.to_next_chunk = function () {
var c = coverage;
// Find the start of the next colored chunk.
var probe = c.sel_end;
var chunk_indicator, probe_line;
while (true) {
probe_line = c.line_elt(probe);
if (probe_line.length === 0) {
return;
}
chunk_indicator = c.chunk_indicator(probe_line);
if (chunk_indicator) {
break;
}
probe++;
}
// There's a next chunk, `probe` points to it.
var begin = probe;
// Find the end of this chunk.
var next_indicator = chunk_indicator;
while (next_indicator === chunk_indicator) {
probe++;
probe_line = c.line_elt(probe);
next_indicator = c.chunk_indicator(probe_line);
}
c.set_sel(begin, probe);
c.show_selection();
};
coverage.to_prev_chunk = function () {
var c = coverage;
// Find the end of the prev colored chunk.
var probe = c.sel_begin-1;
var probe_line = c.line_elt(probe);
if (probe_line.length === 0) {
return;
}
var chunk_indicator = c.chunk_indicator(probe_line);
while (probe > 0 && !chunk_indicator) {
probe--;
probe_line = c.line_elt(probe);
if (probe_line.length === 0) {
return;
}
chunk_indicator = c.chunk_indicator(probe_line);
}
// There's a prev chunk, `probe` points to its last line.
var end = probe+1;
// Find the beginning of this chunk.
var prev_indicator = chunk_indicator;
while (prev_indicator === chunk_indicator) {
probe--;
probe_line = c.line_elt(probe);
prev_indicator = c.chunk_indicator(probe_line);
}
c.set_sel(probe+1, end);
c.show_selection();
};
// Return the line number of the line nearest pixel position pos
coverage.line_at_pos = function (pos) {
var l1 = coverage.line_elt(1),
l2 = coverage.line_elt(2),
result;
if (l1.length && l2.length) {
var l1_top = l1.offset().top,
line_height = l2.offset().top - l1_top,
nlines = (pos - l1_top) / line_height;
if (nlines < 1) {
result = 1;
}
else {
result = Math.ceil(nlines);
}
}
else {
result = 1;
}
return result;
};
// Returns 0, 1, or 2: how many of the two ends of the selection are on
// the screen right now?
coverage.selection_ends_on_screen = function () {
if (coverage.sel_begin === 0) {
return 0;
}
var top = coverage.line_elt(coverage.sel_begin);
var next = coverage.line_elt(coverage.sel_end-1);
return (
(top.isOnScreen() ? 1 : 0) +
(next.isOnScreen() ? 1 : 0)
);
};
coverage.to_next_chunk_nicely = function () {
coverage.finish_scrolling();
if (coverage.selection_ends_on_screen() === 0) {
// The selection is entirely off the screen: select the top line on
// the screen.
var win = $(window);
coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop()));
}
coverage.to_next_chunk();
};
coverage.to_prev_chunk_nicely = function () {
coverage.finish_scrolling();
if (coverage.selection_ends_on_screen() === 0) {
var win = $(window);
coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height()));
}
coverage.to_prev_chunk();
};
// Select line number lineno, or if it is in a colored chunk, select the
// entire chunk
coverage.select_line_or_chunk = function (lineno) {
var c = coverage;
var probe_line = c.line_elt(lineno);
if (probe_line.length === 0) {
return;
}
var the_indicator = c.chunk_indicator(probe_line);
if (the_indicator) {
// The line is in a highlighted chunk.
// Search backward for the first line.
var probe = lineno;
var indicator = the_indicator;
while (probe > 0 && indicator === the_indicator) {
probe--;
probe_line = c.line_elt(probe);
if (probe_line.length === 0) {
break;
}
indicator = c.chunk_indicator(probe_line);
}
var begin = probe + 1;
// Search forward for the last line.
probe = lineno;
indicator = the_indicator;
while (indicator === the_indicator) {
probe++;
probe_line = c.line_elt(probe);
indicator = c.chunk_indicator(probe_line);
}
coverage.set_sel(begin, probe);
}
else {
coverage.set_sel(lineno);
}
};
coverage.show_selection = function () {
var c = coverage;
// Highlight the lines in the chunk
$(".linenos .highlight").removeClass("highlight");
for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) {
c.num_elt(probe).addClass("highlight");
}
c.scroll_to_selection();
};
coverage.scroll_to_selection = function () {
// Scroll the page if the chunk isn't fully visible.
if (coverage.selection_ends_on_screen() < 2) {
// Need to move the page. The html,body trick makes it scroll in all
// browsers, got it from http://stackoverflow.com/questions/3042651
var top = coverage.line_elt(coverage.sel_begin);
var top_pos = parseInt(top.offset().top, 10);
coverage.scroll_window(top_pos - 30);
}
};
coverage.scroll_window = function (to_pos) {
$("html,body").animate({scrollTop: to_pos}, 200);
};
coverage.finish_scrolling = function () {
$("html,body").stop(true, true);
};
coverage.init_scroll_markers = function () {
var c = coverage;
// Init some variables
c.lines_len = $('#source p').length;
c.body_h = $('body').height();
c.header_h = $('div#header').height();
// Build html
c.build_scroll_markers();
};
coverage.build_scroll_markers = function () {
var c = coverage,
min_line_height = 3,
max_line_height = 10,
visible_window_h = $(window).height();
c.lines_to_mark = $('#source').find('p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par');
$('#scroll_marker').remove();
// Don't build markers if the window has no scroll bar.
if (c.body_h <= visible_window_h) {
return;
}
$("body").append("<div id='scroll_marker'>&nbsp;</div>");
var scroll_marker = $('#scroll_marker'),
marker_scale = scroll_marker.height() / c.body_h,
line_height = scroll_marker.height() / c.lines_len;
// Line height must be between the extremes.
if (line_height > min_line_height) {
if (line_height > max_line_height) {
line_height = max_line_height;
}
}
else {
line_height = min_line_height;
}
var previous_line = -99,
last_mark,
last_top,
offsets = {};
// Calculate line offsets outside loop to prevent relayouts
c.lines_to_mark.each(function() {
offsets[this.id] = $(this).offset().top;
});
c.lines_to_mark.each(function () {
var id_name = $(this).attr('id'),
line_top = Math.round(offsets[id_name] * marker_scale),
line_number = parseInt(id_name.substring(1, id_name.length));
if (line_number === previous_line + 1) {
// If this solid missed block just make previous mark higher.
last_mark.css({
'height': line_top + line_height - last_top
});
}
else {
// Add colored line in scroll_marker block.
scroll_marker.append('<div id="m' + line_number + '" class="marker"></div>');
last_mark = $('#m' + line_number);
last_mark.css({
'height': line_height,
'top': line_top
});
last_top = line_top;
}
previous_line = line_number;
});
};

102
htmlcov/exceptions_py.html generated Normal file
View File

@@ -0,0 +1,102 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
<title>Coverage for exceptions.py: 100%</title>
<link rel="stylesheet" href="style.css" type="text/css">
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.hotkeys.js"></script>
<script type="text/javascript" src="jquery.isonscreen.js"></script>
<script type="text/javascript" src="coverage_html.js"></script>
<script type="text/javascript">
jQuery(document).ready(coverage.pyfile_ready);
</script>
</head>
<body class="pyfile">
<div id="header">
<div class="content">
<h1>Coverage for <b>exceptions.py</b> :
<span class="pc_cov">100%</span>
</h1>
<img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<h2 class="stats">
20 statements &nbsp;
<span class="run shortkey_r button_toggle_run">20 run</span>
<span class="mis show_mis shortkey_m button_toggle_mis">0 missing</span>
<span class="exc show_exc shortkey_x button_toggle_exc">0 excluded</span>
</h2>
</div>
</div>
<div class="help_panel">
<img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
<p class="legend">Hot-keys on this page</p>
<div>
<p class="keyhelp">
<span class="key">r</span>
<span class="key">m</span>
<span class="key">x</span>
<span class="key">p</span> &nbsp; toggle line displays
</p>
<p class="keyhelp">
<span class="key">j</span>
<span class="key">k</span> &nbsp; next/prev highlighted chunk
</p>
<p class="keyhelp">
<span class="key">0</span> &nbsp; (zero) top of page
</p>
<p class="keyhelp">
<span class="key">1</span> &nbsp; (one) first highlighted chunk
</p>
</div>
</div>
<div id="source">
<p id="t1" class="run"><span class="n"><a href="#t1">1</a></span><span class="t"><span class="key">class</span> <span class="nam">TooMany</span><span class="op">(</span><span class="nam">Exception</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t2" class="run"><span class="n"><a href="#t2">2</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t3" class="pln"><span class="n"><a href="#t3">3</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t4" class="pln"><span class="n"><a href="#t4">4</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t5" class="run"><span class="n"><a href="#t5">5</a></span><span class="t"><span class="key">class</span> <span class="nam">TooManyPlayers</span><span class="op">(</span><span class="nam">TooMany</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t6" class="run"><span class="n"><a href="#t6">6</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t7" class="pln"><span class="n"><a href="#t7">7</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t8" class="pln"><span class="n"><a href="#t8">8</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t9" class="run"><span class="n"><a href="#t9">9</a></span><span class="t"><span class="key">class</span> <span class="nam">NotEnough</span><span class="op">(</span><span class="nam">Exception</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t10" class="run"><span class="n"><a href="#t10">10</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t11" class="pln"><span class="n"><a href="#t11">11</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t12" class="pln"><span class="n"><a href="#t12">12</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t13" class="run"><span class="n"><a href="#t13">13</a></span><span class="t"><span class="key">class</span> <span class="nam">NotEnoughPlayers</span><span class="op">(</span><span class="nam">NotEnough</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t14" class="run"><span class="n"><a href="#t14">14</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t15" class="pln"><span class="n"><a href="#t15">15</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t16" class="pln"><span class="n"><a href="#t16">16</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t17" class="run"><span class="n"><a href="#t17">17</a></span><span class="t"><span class="key">class</span> <span class="nam">MustBeEqualAmounts</span><span class="op">(</span><span class="nam">Exception</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t18" class="run"><span class="n"><a href="#t18">18</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t19" class="pln"><span class="n"><a href="#t19">19</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t20" class="pln"><span class="n"><a href="#t20">20</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t21" class="run"><span class="n"><a href="#t21">21</a></span><span class="t"><span class="key">class</span> <span class="nam">Argument</span><span class="op">(</span><span class="nam">Exception</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t22" class="run"><span class="n"><a href="#t22">22</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t23" class="pln"><span class="n"><a href="#t23">23</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t24" class="pln"><span class="n"><a href="#t24">24</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t25" class="run"><span class="n"><a href="#t25">25</a></span><span class="t"><span class="key">class</span> <span class="nam">DidntFind</span><span class="op">(</span><span class="nam">Exception</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t26" class="run"><span class="n"><a href="#t26">26</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t27" class="pln"><span class="n"><a href="#t27">27</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t28" class="pln"><span class="n"><a href="#t28">28</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t29" class="run"><span class="n"><a href="#t29">29</a></span><span class="t"><span class="key">class</span> <span class="nam">NoOwner</span><span class="op">(</span><span class="nam">Exception</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t30" class="run"><span class="n"><a href="#t30">30</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t31" class="pln"><span class="n"><a href="#t31">31</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t32" class="pln"><span class="n"><a href="#t32">32</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t33" class="run"><span class="n"><a href="#t33">33</a></span><span class="t"><span class="key">class</span> <span class="nam">CantMortgage</span><span class="op">(</span><span class="nam">Exception</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t34" class="run"><span class="n"><a href="#t34">34</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
<p id="t35" class="pln"><span class="n"><a href="#t35">35</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t36" class="pln"><span class="n"><a href="#t36">36</a></span><span class="t">&nbsp;</span><span class="r"></span></p>
<p id="t37" class="run"><span class="n"><a href="#t37">37</a></span><span class="t"><span class="key">class</span> <span class="nam">CantBuyBuildings</span><span class="op">(</span><span class="nam">Exception</span><span class="op">)</span><span class="op">:</span>&nbsp;</span><span class="r"></span></p>
<p id="t38" class="run"><span class="n"><a href="#t38">38</a></span><span class="t"> <span class="key">pass</span>&nbsp;</span><span class="r"></span></p>
</div>
<div id="footer">
<div class="content">
<p>
<a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="https://coverage.readthedocs.io">coverage.py v5.1</a>,
created at 2020-06-01 17:09
</p>
</div>
</div>
</body>
</html>

91
htmlcov/index.html generated Normal file
View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Coverage report</title>
<link rel="stylesheet" href="style.css" type="text/css">
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.ba-throttle-debounce.min.js"></script>
<script type="text/javascript" src="jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="jquery.hotkeys.js"></script>
<script type="text/javascript" src="coverage_html.js"></script>
<script type="text/javascript">
jQuery(document).ready(coverage.index_ready);
</script>
</head>
<body class="indexfile">
<div id="header">
<div class="content">
<h1>Coverage report:
<span class="pc_cov">50%</span>
</h1>
<img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
<form id="filter_container">
<input id="filter" type="text" value="" placeholder="filter..." />
</form>
</div>
</div>
<div class="help_panel">
<img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
<p class="legend">Hot-keys on this page</p>
<div>
<p class="keyhelp">
<span class="key">n</span>
<span class="key">s</span>
<span class="key">m</span>
<span class="key">x</span>
<span class="key">c</span> &nbsp; change column sorting
</p>
</div>
</div>
<div id="index">
<table class="index">
<thead>
<tr class="tablehead" title="Click to sort">
<th class="name left headerSortDown shortkey_n">Module</th>
<th class="shortkey_s">statements</th>
<th class="shortkey_m">missing</th>
<th class="shortkey_x">excluded</th>
<th class="right shortkey_c">coverage</th>
</tr>
</thead>
<tfoot>
<tr class="total">
<td class="name left">Total</td>
<td>505</td>
<td>250</td>
<td>0</td>
<td class="right" data-ratio="255 505">50%</td>
</tr>
</tfoot>
<tbody>
<tr class="file">
<td class="name left"><a href="exceptions_py.html">exceptions.py</a></td>
<td>20</td>
<td>0</td>
<td>0</td>
<td class="right" data-ratio="20 20">100%</td>
</tr>
<tr class="file">
<td class="name left"><a href="monopoly_py.html">monopoly.py</a></td>
<td>485</td>
<td>250</td>
<td>0</td>
<td class="right" data-ratio="235 485">48%</td>
</tr>
</tbody>
</table>
<p id="no_rows">
No items found using the specified filter.
</p>
</div>
<div id="footer">
<div class="content">
<p>
<a class="nav" href="https://coverage.readthedocs.io">coverage.py v5.1</a>,
created at 2020-06-01 17:10
</p>
</div>
</div>
</body>
</html>

9
htmlcov/jquery.ba-throttle-debounce.min.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
/*
* jQuery throttle / debounce - v1.1 - 3/7/2010
* http://benalman.com/projects/jquery-throttle-debounce-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);

99
htmlcov/jquery.hotkeys.js generated Normal file
View File

@@ -0,0 +1,99 @@
/*
* jQuery Hotkeys Plugin
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Based upon the plugin by Tzury Bar Yochay:
* http://github.com/tzuryby/hotkeys
*
* Original idea by:
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
*/
(function(jQuery){
jQuery.hotkeys = {
version: "0.8",
specialKeys: {
8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
},
shiftNums: {
"`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
"8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
".": ">", "/": "?", "\\": "|"
}
};
function keyHandler( handleObj ) {
// Only care when a possible input has been specified
if ( typeof handleObj.data !== "string" ) {
return;
}
var origHandler = handleObj.handler,
keys = handleObj.data.toLowerCase().split(" ");
handleObj.handler = function( event ) {
// Don't fire in text-accepting inputs that we didn't directly bind to
if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
event.target.type === "text") ) {
return;
}
// Keypress represents characters, not special keys
var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
character = String.fromCharCode( event.which ).toLowerCase(),
key, modif = "", possible = {};
// check combinations (alt|ctrl|shift+anything)
if ( event.altKey && special !== "alt" ) {
modif += "alt+";
}
if ( event.ctrlKey && special !== "ctrl" ) {
modif += "ctrl+";
}
// TODO: Need to make sure this works consistently across platforms
if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
modif += "meta+";
}
if ( event.shiftKey && special !== "shift" ) {
modif += "shift+";
}
if ( special ) {
possible[ modif + special ] = true;
} else {
possible[ modif + character ] = true;
possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
// "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
if ( modif === "shift+" ) {
possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
}
}
for ( var i = 0, l = keys.length; i < l; i++ ) {
if ( possible[ keys[i] ] ) {
return origHandler.apply( this, arguments );
}
}
};
}
jQuery.each([ "keydown", "keyup", "keypress" ], function() {
jQuery.event.special[ this ] = { add: keyHandler };
});
})( jQuery );

53
htmlcov/jquery.isonscreen.js generated Normal file
View File

@@ -0,0 +1,53 @@
/* Copyright (c) 2010
* @author Laurence Wheway
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
*
* @version 1.2.0
*/
(function($) {
jQuery.extend({
isOnScreen: function(box, container) {
//ensure numbers come in as intgers (not strings) and remove 'px' is it's there
for(var i in box){box[i] = parseFloat(box[i])};
for(var i in container){container[i] = parseFloat(container[i])};
if(!container){
container = {
left: $(window).scrollLeft(),
top: $(window).scrollTop(),
width: $(window).width(),
height: $(window).height()
}
}
if( box.left+box.width-container.left > 0 &&
box.left < container.width+container.left &&
box.top+box.height-container.top > 0 &&
box.top < container.height+container.top
) return true;
return false;
}
})
jQuery.fn.isOnScreen = function (container) {
for(var i in container){container[i] = parseFloat(container[i])};
if(!container){
container = {
left: $(window).scrollLeft(),
top: $(window).scrollTop(),
width: $(window).width(),
height: $(window).height()
}
}
if( $(this).offset().left+$(this).width()-container.left > 0 &&
$(this).offset().left < container.width+container.left &&
$(this).offset().top+$(this).height()-container.top > 0 &&
$(this).offset().top < container.height+container.top
) return true;
return false;
}
})(jQuery);

4
htmlcov/jquery.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

2
htmlcov/jquery.tablesorter.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

BIN
htmlcov/keybd_closed.png generated Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

BIN
htmlcov/keybd_open.png generated Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

1166
htmlcov/monopoly_py.html generated Normal file

File diff suppressed because it is too large Load Diff

1
htmlcov/status.json generated Normal file
View File

@@ -0,0 +1 @@
{"format":2,"version":"5.1","globals":"36313e81643a3db58cf1124f7f45ff7f","files":{"exceptions_py":{"hash":"24aac4ad890cd9eca3b48e798198f5aa","index":{"nums":[1,20,0,0,0,0,0],"html_filename":"exceptions_py.html","relative_filename":"exceptions.py"}},"monopoly_py":{"hash":"045e6cfe9ae9a903be5f67f8a34baa4d","index":{"nums":[1,485,0,250,0,0,0],"html_filename":"monopoly_py.html","relative_filename":"monopoly.py"}}}}

124
htmlcov/style.css generated Normal file
View File

@@ -0,0 +1,124 @@
@charset "UTF-8";
/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
/* Don't edit this .css file. Edit the .scss file instead! */
html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
body { font-family: georgia, serif; font-size: 1em; }
html > body { font-size: 16px; }
p { font-size: .75em; line-height: 1.33333333em; }
table { border-collapse: collapse; }
td { vertical-align: top; }
table tr.hidden { display: none !important; }
p#no_rows { display: none; font-size: 1.2em; }
a.nav { text-decoration: none; color: inherit; }
a.nav:hover { text-decoration: underline; color: inherit; }
#header { background: #f8f8f8; width: 100%; border-bottom: 1px solid #eee; }
.indexfile #footer { margin: 1em 3em; }
.pyfile #footer { margin: 1em 1em; }
#footer .content { padding: 0; font-size: 85%; font-family: verdana, sans-serif; color: #666666; font-style: italic; }
#index { margin: 1em 0 0 3em; }
#header .content { padding: 1em 3rem; }
h1 { font-size: 1.25em; display: inline-block; }
#filter_container { display: inline-block; float: right; margin: 0 2em 0 0; }
#filter_container input { width: 10em; }
h2.stats { margin-top: .5em; font-size: 1em; }
.stats span { border: 1px solid; border-radius: .1em; padding: .1em .5em; margin: 0 .1em; cursor: pointer; border-color: #ccc #999 #999 #ccc; }
.stats span.run { background: #eeffee; }
.stats span.run.show_run { border-color: #999 #ccc #ccc #999; background: #ddffdd; }
.stats span.mis { background: #ffeeee; }
.stats span.mis.show_mis { border-color: #999 #ccc #ccc #999; background: #ffdddd; }
.stats span.exc { background: #f7f7f7; }
.stats span.exc.show_exc { border-color: #999 #ccc #ccc #999; background: #eeeeee; }
.stats span.par { background: #ffffd5; }
.stats span.par.show_par { border-color: #999 #ccc #ccc #999; background: #ffffaa; }
#source p .annotate.long, .help_panel { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; box-shadow: #cccccc .2em .2em .2em; color: #333; padding: .25em .5em; }
#source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; }
#keyboard_icon { float: right; margin: 5px; cursor: pointer; }
.help_panel { padding: .5em; border: 1px solid #883; }
.help_panel .legend { font-style: italic; margin-bottom: 1em; }
.indexfile .help_panel { width: 20em; height: 4em; }
.pyfile .help_panel { width: 16em; height: 8em; }
#panel_icon { float: right; cursor: pointer; }
.keyhelp { margin: .75em; }
.keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: monospace; font-weight: bold; background: #eee; }
#source { padding: 1em 0 1em 3rem; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; }
#source p { position: relative; white-space: pre; }
#source p * { box-sizing: border-box; }
#source p .n { float: left; text-align: right; width: 3rem; box-sizing: border-box; margin-left: -3rem; padding-right: 1em; color: #999999; font-family: verdana, sans-serif; }
#source p .n a { text-decoration: none; color: #999999; font-size: .8333em; line-height: 1em; }
#source p .n a:hover { text-decoration: underline; color: #999999; }
#source p.highlight .n { background: #ffdd00; }
#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid white; }
#source p .t:hover { background: #f2f2f2; }
#source p .t:hover ~ .r .annotate.long { display: block; }
#source p .t .com { color: green; font-style: italic; line-height: 1px; }
#source p .t .key { font-weight: bold; line-height: 1px; }
#source p .t .str { color: #000080; }
#source p.mis .t { border-left: 0.2em solid #ff0000; }
#source p.mis.show_mis .t { background: #ffdddd; }
#source p.mis.show_mis .t:hover { background: #f2d2d2; }
#source p.run .t { border-left: 0.2em solid #00ff00; }
#source p.run.show_run .t { background: #ddffdd; }
#source p.run.show_run .t:hover { background: #d2f2d2; }
#source p.exc .t { border-left: 0.2em solid #808080; }
#source p.exc.show_exc .t { background: #eeeeee; }
#source p.exc.show_exc .t:hover { background: #e2e2e2; }
#source p.par .t { border-left: 0.2em solid #eeee99; }
#source p.par.show_par .t { background: #ffffaa; }
#source p.par.show_par .t:hover { background: #f2f2a2; }
#source p .r { position: absolute; top: 0; right: 2.5em; font-family: verdana, sans-serif; }
#source p .annotate { font-family: georgia; color: #666; padding-right: .5em; }
#source p .annotate.short:hover ~ .long { display: block; }
#source p .annotate.long { width: 30em; right: 2.5em; }
#source p input { display: none; }
#source p input ~ .r label.ctx { cursor: pointer; border-radius: .25em; }
#source p input ~ .r label.ctx::before { content: "▶ "; }
#source p input ~ .r label.ctx:hover { background: #d5f7ff; color: #666; }
#source p input:checked ~ .r label.ctx { background: #aaeeff; color: #666; border-radius: .75em .75em 0 0; padding: 0 .5em; margin: -.25em 0; }
#source p input:checked ~ .r label.ctx::before { content: "▼ "; }
#source p input:checked ~ .ctxs { padding: .25em .5em; overflow-y: scroll; max-height: 10.5em; }
#source p label.ctx { color: #999; display: inline-block; padding: 0 .5em; font-size: .8333em; }
#source p .ctxs { display: block; max-height: 0; overflow-y: hidden; transition: all .2s; padding: 0 .5em; font-family: verdana, sans-serif; white-space: nowrap; background: #aaeeff; border-radius: .25em; margin-right: 1.75em; }
#source p .ctxs span { display: block; text-align: right; }
#index td, #index th { text-align: right; width: 5em; padding: .25em .5em; border-bottom: 1px solid #eee; }
#index td.left, #index th.left { padding-left: 0; }
#index td.right, #index th.right { padding-right: 0; }
#index td.name, #index th.name { text-align: left; width: auto; }
#index th { font-style: italic; color: #333; border-bottom: 1px solid #ccc; cursor: pointer; }
#index th:hover { background: #eee; border-bottom: 1px solid #999; }
#index th.headerSortDown, #index th.headerSortUp { border-bottom: 1px solid #000; white-space: nowrap; background: #eee; }
#index th.headerSortDown:after { content: " ↓"; }
#index th.headerSortUp:after { content: " ↑"; }
#index td.name a { text-decoration: none; color: #000; }
#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; }
#index tr.file:hover { background: #eeeeee; }
#index tr.file:hover td.name { text-decoration: underline; color: #000; }
#scroll_marker { position: fixed; right: 0; top: 0; width: 16px; height: 100%; background: white; border-left: 1px solid #eee; will-change: transform; }
#scroll_marker .marker { background: #ddd; position: absolute; min-height: 3px; width: 100%; }

View File

@@ -4,31 +4,63 @@ Purpose:
1) To simulate games of Monopoly in order to determine the best strategy
2) To play Monopoly on a computer
Along those lines, here's some prior art:
http://www.tkcs-collins.com/truman/monopoly/monopoly.shtml
https://blog.ed.ted.com/2017/12/01/heres-how-to-win-at-monopoly-according-to-math-experts/
# TODO: maybe instead of all these classmethods, instances?
# TODO: something more graceful than Game.games[0]
# TODO: store LAST_ROLL in a global constant instead of passing it around to all the `action` methods
TODO: maybe instead of all these classmethods, instances?
TODO: something more graceful than Game.games[0]
TODO: store LAST_ROLL in a global constant instead of passing it around to all the `action` methods
TODO: write some tests
TODO: break up into modules
TODO: add auctions
TODO: print the reason someone decided to/not to buy a property
TODO: print whether someone is in jail or just visiting
TODO: don't allow buying of multiple houses/a hotel on a property if the other properties in the same color don't have any
"""
from abc import abstractmethod, ABC
from abc import ABC
from collections import defaultdict
from itertools import cycle
from random import shuffle, choice
from random import choice, shuffle
from time import sleep
from typing import Type, NewType, Tuple, cast, List
from typing import cast, List, NewType, Optional, Tuple, Type
from exceptions import TooManyPlayers, NotEnough, DidntFind, Argument, NoOwner, CantMortgage, \
CantBuyBuildings, TooMany, MustBeEqualAmounts
from exceptions import (
Argument,
CantBuyBuildings,
CantMortgage,
DidntFind,
MustBeEqualAmounts,
NoOwner,
NotEnough,
TooMany,
TooManyPlayers,
NotEnoughPlayers,
)
ALL_MONEY = 20_580
NUM_HOUSES = 32
NUM_HOTELS = 12
Doubles = NewType("Doubles", bool)
LANGUAGE = "français"
BACKUP_LANGUAGE = "English"
BUILDING_TYPES = "house", "hotel"
MAX_ROUNDS = 5000
BUILDABLE_PROPERTY_COLORS = (
"yellow",
"red",
"light blue",
"brown",
"pink",
"orange",
"green",
"dark blue",
)
class Space:
@@ -45,6 +77,10 @@ class NothingHappensWhenYouLandOnItSpace(Space):
class Go(NothingHappensWhenYouLandOnItSpace):
"""
should this really be a NothingHappensWhenYouLandOnItSpace?
since you get to collect $200
"""
pass
@@ -61,6 +97,7 @@ class TaxSpace(Space):
@classmethod
def action(cls, player, _):
print(f"{player} pays bank {cls.amount} ({cls.__name__})")
player.pay("Bank", cls.amount)
@@ -123,8 +160,10 @@ class ElectedPresidentCard(Card):
@classmethod
def action(cls, player, _):
print("{cls.__name__}")
for other_player in Game.games[0].active_players:
if other_player != player:
print(f"{player} is paying {other_player} 50")
player.pay(other_player, 50)
@@ -136,6 +175,7 @@ class GetOutOfJailFreeCard(Card):
@classmethod
def action(cls, player: "Player", _):
print(f"{player} got a get out of jail free card")
player.get_out_of_jail_free_card = True
@@ -145,6 +185,7 @@ class AdvanceCard(Card):
@classmethod
def action(cls, player, _):
print(f"the card is {cls.__name__}")
player.advance(**cls.kwarg)
@@ -186,6 +227,7 @@ class BuildingAndLoanMaturesCard(Card):
@classmethod
def action(self, player, _):
print(f"{self.__class__}: the bank pays {player} 150")
Bank.pay(player, 150)
@@ -195,18 +237,25 @@ class SpeedingCard(Card):
@classmethod
def action(cls, player, _):
print(f"{cls.__name__}: {player} pays 15 to Bank")
player.pay(Bank, 15)
class RepairPropertyCard(Card):
text = {
"français": "Vous faites des réparations sur toutes vos propriétés: Versez M25"
" pour chaque maison M100 pour Chque hôtel que vous possédez"
" pour chaque maison M100 pour Chaque hôtel que vous possédez"
}
@classmethod
def action(cls, player, _):
num_houses, num_hotels = 0, 0
for property in player.buildable_properties:
num_houses += property.buildings["house"]
num_hotels += property.buildings["hotel"]
total_owed = sum([num_houses * 25, num_hotels * 100])
print(f"{player} pays the bank {total_owed} for {cls.__name__}")
player.pay(Bank, total_owed)
class Deck:
@@ -247,8 +296,6 @@ def shuffle_decks():
def buy_decision(property: "Property", player: "Player"):
if property.cost > player.assets:
print(f"{player} doesn't have enough to buy {property}.")
return Game.games[0].buy_decision_algorithm(property, player)
@@ -269,9 +316,14 @@ class Property(Space):
def __repr__(self):
if hasattr(self, "_name"):
return self._name
return self._name[LANGUAGE]
return str(self.__class__)
@classmethod
def reset(cls):
for property in cls.instances:
property.owner = None
@classmethod
def get_num_of_type(cls, type):
return len(cls.instances_by_type()[type])
@@ -295,10 +347,16 @@ class Property(Space):
if not self.owner:
buy = buy_decision(self, player)
if buy:
print(f"{player} will buy {self}")
return player.buy(self)
if self.mortgaged:
print(f"{player} decided not to buy {self}")
return
return player.pay(self.owner, self.calculate_rent(last_roll))
if self.owner == player or self.mortgaged:
print(f"{player} landed on his own property, {self}")
return
rent = self.calculate_rent(last_roll)
print(f"{player} pays {self.owner} ${rent} after landing on it.")
player.pay(self.owner, rent)
def calculate_rent(self, _):
if not self.owner:
@@ -320,7 +378,7 @@ class Utility(Property):
super().calculate_rent(last_roll)
if not last_roll:
return 10 * Player.roll_the_dice()[0]
return self.rent[self.owner.owns_x_of_type(self)](last_roll)
return self.rent[self.owner.owns_x_of_type(self.type)](last_roll)
class Railroad(Property):
@@ -332,7 +390,7 @@ class Railroad(Property):
def calculate_rent(self, _):
super().calculate_rent(_)
owns_x_of_type = self.owner.owns_x_of_type(self)
owns_x_of_type = self.owner.owns_x_of_type(self.type)
if not owns_x_of_type:
return 0
return self.rent[owns_x_of_type]
@@ -359,7 +417,7 @@ class BuildableProperty(Property):
self.unmortgage_cost = unmortgage_cost
self.buildings = {"house": 0, "hotel": 0}
def buy_buildings(self, building_type, quantity=None):
def buy_building(self, building_type):
"""
TODO: Each property within a group must be no more than one house level away from all other
properties in that group. For example, if you own the Orange group, you cant put a
@@ -369,17 +427,15 @@ class BuildableProperty(Property):
"""
if not self.owner.owns_all_type(self.type):
raise CantBuyBuildings
quantity = quantity or 1
if building_type == "hotel" and self.buildings["house"] != 4:
raise NotEnough
elif building_type == "house" and (self.buildings["house"] + quantity) > 4:
elif building_type == "house" and self.buildings["house"] == 4:
raise TooMany
total_buildings = quantity * self.num_of_type
cost = self.house_and_hotel_cost * total_buildings
cost = self.house_and_hotel_cost
self.owner.check_funds(cost)
Bank.get_buildings(building_type, total_buildings)
Bank.get_building(building_type)
self.owner.pay(Bank, cost)
for property_ in self.properties_of_type:
@@ -387,12 +443,14 @@ class BuildableProperty(Property):
property_.buildings["house"] = 0
property_.buildings["hotel"] = 1
else:
property_.buildings["house"] += quantity
property_.buildings["house"] += 1
def sell_buildings(self, building_type, quantity):
if not self.buildings[building_type]:
raise NotEnough
if quantity % self.num_of_type:
# TODO: this isn't right
# https://www.quora.com/When-can-a-player-place-a-house-in-monopoly
raise MustBeEqualAmounts
def mortgage(self, player: "Player"):
@@ -407,8 +465,12 @@ class BuildableProperty(Property):
def calculate_rent(self, _):
super().calculate_rent(_)
if self.buildings:
key = self.buildings
if self.buildings["house"] or self.buildings["hotel"]:
buildings = self.buildings
if buildings["house"]:
key = buildings["house"]
else:
key = "hotel"
elif self.owner.owns_all_type(self.type):
key = "monopoly"
else:
@@ -441,7 +503,7 @@ class Board:
IncomeTax,
Railroad(_name={"français": "Union des Chemins de Fer Privés"}),
BuildableProperty(
_name={"deutsche": "Aarau Rathausplatz"},
_name={"français": "Aarau Rathausplatz"},
cost=100,
color="light blue",
rent={0: 6, "monopoly": 12, 1: 30, 2: 90, 3: 270, 4: 400, "hotel": 550},
@@ -478,7 +540,7 @@ class Board:
mortgage_cost=70,
unmortgage_cost=77,
),
Utility(_name="Usines Électriques"),
Utility(_name={"français": "Usines Électriques"}),
BuildableProperty(
_name={"français": "Soleure Hauptgasse"},
cost=140,
@@ -489,7 +551,7 @@ class Board:
unmortgage_cost=77,
),
BuildableProperty(
_name={"italian": "Lugano Via Nassa"},
_name={"français": "Lugano Via Nassa"},
cost=160,
color="pink",
rent={0: 12, "monopoly": 24, 1: 60, 2: 180, 3: 500, 4: 700, "hotel": 900},
@@ -562,7 +624,7 @@ class Board:
mortgage_cost=120,
unmortgage_cost=132,
),
Railroad(_name="Tramways Interurbains"),
Railroad(_name={"français": "Tramways Interurbains"}),
BuildableProperty(
_name={"français": "Lucerne Weggisgasse"},
cost=260,
@@ -597,7 +659,7 @@ class Board:
mortgage_cost=130,
unmortgage_cost=143,
),
Utility(_name="Usines Hydrauliques"),
Utility(_name={"français": "Usines Hydrauliques"}),
BuildableProperty(
_name={"français": "Lausanne Rue de Bourg"},
cost=280,
@@ -668,7 +730,7 @@ class Board:
mortgage_cost=160,
unmortgage_cost=176,
),
Railroad(_name="Association des Télépheriques"),
Railroad(_name={"français": "Association des Télépheriques"}),
Chance,
BuildableProperty(
_name={"français": "Lausanne Place St. François"},
@@ -726,36 +788,35 @@ def get_space_index(name):
class EconomicActor:
def __repr__(self):
return f"<{self.name} money=${self.money}>"
pass
class Bank(EconomicActor):
name = "Bank"
money = 20_580
NUM_HOUSES = 32
NUM_HOTELS = 12
money = ALL_MONEY
NUM_HOUSES = NUM_HOUSES
NUM_HOTELS = NUM_HOTELS
@classmethod
def reset(cls):
cls.money = ALL_MONEY
cls.NUM_HOUSES = NUM_HOUSES
cls.NUM_HOTELS = NUM_HOTELS
@classmethod
def pay(cls, actor: "EconomicActor", amount: int):
if isinstance(actor, str):
actor = eval(actor)
if amount > cls.money:
raise NotEnough
cls.money -= amount
actor.money += amount
@classmethod
def get_buildings(cls, type_, quantity=None):
def get_building(cls, type_):
cls.check_building_type(type_)
to_check = cls.get_building_store(type_)
if type_ == "hotel":
quantity = 1
if to_check < quantity:
store = cls.get_building_store(type_)
if not store:
raise NotEnough(f"Not enough {type_}s!")
else:
to_check -= quantity
store -= 1
@classmethod
def put_building(cls, type_, quantity):
@@ -800,6 +861,60 @@ class GetOutOfJailDecision(Decision):
pass
def get_property_with_least_number_of_houses(properties):
return sorted(properties, key=lambda prop: prop.buildings["house"], reverse=True)[0]
def get_property_with_no_hotels(properties):
return sorted(properties, key=lambda prop: prop.buildings["hotel"])[0]
class Monopoly:
def __init__(self, property_: "BuildableProperty"):
self.properties = Property.instances_by_type()[property_.type]
self.num_properties = len(self.properties)
self.max_num_houses = 4 * self.num_properties
self.max_num_hotels = self.num_properties
def __repr__(self):
return f"<Monopoly type={self.properties[0].type}"
@property
def num_houses(self):
return sum(property.buildings["house"] for property in self.properties)
@property
def num_hotels(self):
return sum(property.buildings["hotel"] for property in self.properties)
@property
def next_building(self) -> Tuple[Optional[str], Optional["BuildableProperty"]]:
num_houses, num_hotels, max_num_houses, max_num_hotels = (
self.num_houses,
self.num_hotels,
self.max_num_houses,
self.max_num_hotels,
)
first_prop = self.properties[0]
if not num_houses and not num_hotels:
return "house", first_prop
elif num_hotels == max_num_hotels:
return None, None
elif num_houses < max_num_houses:
if not num_hotels:
return (
"house",
get_property_with_least_number_of_houses(self.properties),
)
else:
return "hotel", get_property_with_no_hotels(self.properties)
elif num_houses == max_num_houses:
return "hotel", first_prop
class Player(EconomicActor):
in_jail = False
bankrupt = False
@@ -807,17 +922,20 @@ class Player(EconomicActor):
go_again = False
current_space_index = get_space_index("Go")
money = 0
passed_go_times = 0
monopolies = []
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def __init__(self):
self.name = choice([str(i) for i in range(10_000)])
Bank.pay(self, 1_500)
def pay(self, actor: Type["EconomicActor"], amount: int):
if isinstance(actor, str):
actor = eval(actor)
print(actor, amount, self.money)
self.check_funds(amount)
print(f"{self} is paying {actor} ${amount}")
self.money -= amount
actor.money += amount
@@ -829,16 +947,46 @@ class Player(EconomicActor):
try:
self.pay(from_, cost or property_.cost)
except NotEnough:
print(f"{self.name} does not have enough to buy {property_._name}")
return
property_.owner = self
print(f"{self.name} bought {property_._name}")
sleep(0.2)
if property_.__class__.__name__ == "BuildableProperty" and self.owns_all_type(
property_.type
):
monopoly = Monopoly(property_)
self.monopolies.append(monopoly)
def buy_buildings_if_possible(self):
if self.monopolies:
print(f"{self} has {self.monopolies}")
else:
print(f"{self} has no monopolies.")
for monopoly in self.monopolies:
while True:
next_building_type, property_ = monopoly.next_building
if not next_building_type:
break
print("next_building_type:", next_building_type, "property_:", property_)
if not self.can_afford(property_.house_and_hotel_cost):
print("can't afford")
break
try:
property_.buy_building(next_building_type)
except NotEnough:
print("can't afford")
break
print("bought a building")
def take_a_turn(self):
if self.in_jail:
return GetOutOfJailDecision(self)
print(f"{self} is in jail")
decision = GetOutOfJailDecision(self)
print(decision)
return decision
# TODO: you can buy buildings from jail! Fix this
self.buy_buildings_if_possible()
num_spaces, doubles = self.roll_the_dice()
print(f'{self} rolled', str(num_spaces))
if doubles:
self.go_again = True
else:
@@ -848,12 +996,14 @@ class Player(EconomicActor):
def owns_x_of_type(self, type_):
properties_of_this_type = self.properties_by_type.get(type_)
if properties_of_this_type is None:
return 0
if properties_of_this_type is None:
return 0
return len(properties_of_this_type)
def owns_all_type(self, type_):
return self.owns_x_of_type(type_) == Property.get_num_of_type(type)
return self.owns_x_of_type(type_) == Property.get_num_of_type(type_)
@property
def properties_by_type(self):
@@ -884,6 +1034,12 @@ class Player(EconomicActor):
# then iterate through those instances to see which ones have an owner equal to `self`
return [p for p in Property.instances if p.owner == self]
@property
def buildable_properties(self) -> List[BuildableProperty]:
return [
p for p in self.properties if p.__class__.__name__ == "BuildableProperty"
]
def advance(
self,
num_spaces=None,
@@ -907,10 +1063,15 @@ class Player(EconomicActor):
if pass_go and new_space_index >= Board.NUM_SPACES - 1:
self.money += 200
print(f"{self} passed go and collected 200")
self.passed_go_times += 1
new_space_index = new_space_index - Board.NUM_SPACES
elif pass_go and self.current_space_index > new_space_index:
self.money += 200
print(f"{self} passed go and collected 200")
self.passed_go_times += 1
print("new_space_index", str(new_space_index))
self.current_space_index = new_space_index
if just_rolled:
@@ -923,55 +1084,69 @@ class Player(EconomicActor):
except NotEnough:
# TODO: is this always right?
# TODO: eventually make deals and mortgage prtoperties to avoid bankruptcy
print(f"{self.name} went bankrupt!")
self.bankrupt = True
print(f"{self} just went bankrupt!")
def do_action_of_current_space(self, last_roll=None):
space = Board.spaces[self.current_space_index]
print(f"space is {space}")
space.action(self, last_roll)
def go_to_jail(self):
self.in_jail = True
self.current_space_index = get_space_index("Jail")
def can_afford(self, cost):
return self.money >= cost
class Game:
games = []
rounds = 0
def __init__(self, *player_names, buy_decision_algorithm=None):
def return_true(_, __):
return True
self.buy_decision_algorithm = buy_decision_algorithm or return_true
def __init__(self, num_players, buy_decision_algorithm, slow_down=False):
self.slow_down = slow_down
Bank.reset()
shuffle_decks()
Property.reset()
self.buy_decision_algorithm = buy_decision_algorithm()
# TODO: make this nicer
if self.games:
del self.games[0]
self.games.append(self)
if len(player_names) > 8:
if num_players < 2:
raise NotEnoughPlayers
if num_players > 8:
raise TooManyPlayers
self._players = [Player(player_name) for player_name in player_names]
self._players = [Player() for _ in range(num_players)]
self.players = cycle(self._players)
# TODO: roll to see who goes first, then order the players accordingly
shuffle_decks()
self.next()
self.start()
@property
def active_players(self):
return [player for player in self._players if not player.bankrupt]
def next(self):
while len(self.active_players) > 1:
def start(self):
while len(self.active_players) > 1 and self.rounds < MAX_ROUNDS:
current_player = next(self.players)
if not current_player.bankrupt:
if self.slow_down:
sleep(3)
print()
print()
current_player.take_a_turn()
while current_player.go_again:
while current_player.go_again and not current_player.bankrupt:
if self.slow_down:
sleep(3)
print()
print()
current_player.take_a_turn()
self.rounds += 1
if self.rounds > 500:
break
self.end()
def get_rounds_played_per_player(self):
return self.rounds / len(self._players)
def end(self):
print(self.active_players[0], "is the winner!")
print(f"It took {self.rounds} rounds.")
game = Game("Bot", "Ro", "Francis", buy_decision_algorithm=None)
for player in self._players:
del player

49
simulate.py Normal file
View File

@@ -0,0 +1,49 @@
import random
import statistics
from collections import defaultdict
from monopoly import Game
from buy_decision_algos import BuyIfNoOneOwnsTypeAndIsOfTheOneTypeOwned
def get_results(results, game, attrs_to_get):
for attr in attrs_to_get:
attr_obj = getattr(game, attr)
if callable(attr_obj):
val = attr_obj()
else:
val = attr_obj
results[attr].append(val)
def print_results(results, num_players):
for attr, results_list in results.items():
mean = statistics.mean(results_list)
std_dev = statistics.stdev(results_list)
print(
f"num_players -> {num_players}, mean -> {int(mean)}, stdev -> {int(std_dev)}"
)
def play_x_games(
num_games=200,
num_players=range(2, 9),
buy_decision_algorithms=(BuyIfNoOneOwnsTypeAndIsOfTheOneTypeOwned,),
attrs_to_get=("get_rounds_played_per_player",),
slow_down=False,
):
for buy_decision_algorithm in buy_decision_algorithms:
results = defaultdict(list)
print(buy_decision_algorithm.__name__)
print(buy_decision_algorithm.__doc__)
print("num games per simulation:", str(num_games))
print("attrs to get:", attrs_to_get)
for num_players_ in num_players:
for i in range(num_games):
game = Game(num_players_, buy_decision_algorithm=buy_decision_algorithm, slow_down=slow_down)
get_results(results, game, attrs_to_get)
game.end()
print_results(results, num_players_)

316
tags Normal file
View File

@@ -0,0 +1,316 @@
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
!_TAG_PROGRAM_NAME Exuberant Ctags //
!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
!_TAG_PROGRAM_VERSION 5.9~svn20110310 //
ALL_MONEY monopoly.py /^ALL_MONEY = 20_580$/;" v
AdvanceCard monopoly.py /^class AdvanceCard(Card):$/;" c
AdvanceThreeSpacesCard monopoly.py /^class AdvanceThreeSpacesCard(AdvanceCard):$/;" c
Argument exceptions.py /^class Argument(Exception):$/;" c
BACKUP_LANGUAGE monopoly.py /^BACKUP_LANGUAGE = "English"$/;" v
BUILDABLE_PROPERTY_COLORS monopoly.py /^BUILDABLE_PROPERTY_COLORS = ($/;" v
BUILDING_TYPES monopoly.py /^BUILDING_TYPES = "house", "hotel"$/;" v
Bank monopoly.py /^class Bank(EconomicActor):$/;" c
Board monopoly.py /^class Board:$/;" c
BuildableProperty monopoly.py /^class BuildableProperty(Property):$/;" c
BuildingAndLoanMaturesCard monopoly.py /^class BuildingAndLoanMaturesCard(Card):$/;" c
BuyDecision buy_decision_algos.py /^class BuyDecision(ABC):$/;" c
BuyEverything buy_decision_algos.py /^class BuyEverything(BuyDecision):$/;" c
BuyIfDontHaveTwoPartialMonopoliesOfOtherColors buy_decision_algos.py /^class BuyIfDontHaveTwoPartialMonopoliesOfOtherColors(BuyDecision):$/;" c
BuyIfHaveThreeTimesPrice buy_decision_algos.py /^class BuyIfHaveThreeTimesPrice(BuyDecision):$/;" c
BuyIfNoOneOwnsTypeAndIsOfTheOneTypeOwned buy_decision_algos.py /^class BuyIfNoOneOwnsTypeAndIsOfTheOneTypeOwned(BuyDecision):$/;" c
BuyIfOwnFewerThanFivePropertiesOrHaveOneOfThisColor buy_decision_algos.py /^class BuyIfOwnFewerThanFivePropertiesOrHaveOneOfThisColor(BuyDecision):$/;" c
CantBuyBuildings exceptions.py /^class CantBuyBuildings(Exception):$/;" c
CantMortgage exceptions.py /^class CantMortgage(Exception):$/;" c
Card monopoly.py /^class Card(ABC):$/;" c
CardSpace monopoly.py /^class CardSpace(Space):$/;" c
Chance monopoly.py /^class Chance(CardSpace):$/;" c
ChanceDeck monopoly.py /^class ChanceDeck(Deck):$/;" c
CommunityChest monopoly.py /^class CommunityChest(CardSpace):$/;" c
CommunityChestDeck monopoly.py /^class CommunityChestDeck(Deck):$/;" c
Decision monopoly.py /^class Decision:$/;" c
Deck monopoly.py /^class Deck:$/;" c
DidntFind exceptions.py /^class DidntFind(Exception):$/;" c
Doubles monopoly.py /^Doubles = NewType("Doubles", bool)$/;" v
EconomicActor monopoly.py /^class EconomicActor:$/;" c
ElectedPresidentCard monopoly.py /^class ElectedPresidentCard(Card):$/;" c
FreeParking monopoly.py /^class FreeParking(NothingHappensWhenYouLandOnItSpace):$/;" c
Game monopoly.py /^class Game:$/;" c
GetOutOfJailDecision monopoly.py /^class GetOutOfJailDecision(Decision):$/;" c
GetOutOfJailFreeCard monopoly.py /^class GetOutOfJailFreeCard(Card):$/;" c
Go monopoly.py /^class Go(NothingHappensWhenYouLandOnItSpace):$/;" c
GoToBernPlaceFederaleCard monopoly.py /^class GoToBernPlaceFederaleCard(AdvanceCard):$/;" c
GoToClosestRailroadCard monopoly.py /^class GoToClosestRailroadCard(AdvanceCard):$/;" c
GoToJail monopoly.py /^class GoToJail(Space):$/;" c
GoToJailCard monopoly.py /^class GoToJailCard(AdvanceCard):$/;" c
IncomeTax monopoly.py /^class IncomeTax(TaxSpace):$/;" c
Jail monopoly.py /^class Jail(NothingHappensWhenYouLandOnItSpace):$/;" c
LANGUAGE monopoly.py /^LANGUAGE = "français"$/;" v
LuxuryTax monopoly.py /^class LuxuryTax(TaxSpace):$/;" c
MAX_ROUNDS monopoly.py /^MAX_ROUNDS = 5000$/;" v
Monopoly monopoly.py /^class Monopoly:$/;" c
MustBeEqualAmounts exceptions.py /^class MustBeEqualAmounts(Exception):$/;" c
NUM_HOTELS monopoly.py /^ NUM_HOTELS = NUM_HOTELS$/;" v class:Bank
NUM_HOTELS monopoly.py /^NUM_HOTELS = 12$/;" v
NUM_HOUSES monopoly.py /^ NUM_HOUSES = NUM_HOUSES$/;" v class:Bank
NUM_HOUSES monopoly.py /^NUM_HOUSES = 32$/;" v
NUM_SPACES monopoly.py /^ NUM_SPACES = len(spaces)$/;" v class:Board
NoOwner exceptions.py /^class NoOwner(Exception):$/;" c
NotEnough exceptions.py /^class NotEnough(Exception):$/;" c
NotEnoughPlayers exceptions.py /^class NotEnoughPlayers(NotEnough):$/;" c
NothingHappensWhenYouLandOnItSpace monopoly.py /^class NothingHappensWhenYouLandOnItSpace(Space):$/;" c
Player monopoly.py /^class Player(EconomicActor):$/;" c
Property monopoly.py /^class Property(Space):$/;" c
Railroad monopoly.py /^class Railroad(Property):$/;" c
RepairPropertyCard monopoly.py /^class RepairPropertyCard(Card):$/;" c
SPACES_DICT monopoly.py /^ SPACES_DICT = {}$/;" v class:Board
Space monopoly.py /^class Space:$/;" c
SpeedingCard monopoly.py /^class SpeedingCard(Card):$/;" c
TaxSpace monopoly.py /^class TaxSpace(Space):$/;" c
TooMany exceptions.py /^class TooMany(Exception):$/;" c
TooManyPlayers exceptions.py /^class TooManyPlayers(TooMany):$/;" c
Utility monopoly.py /^class Utility(Property):$/;" c
__call__ buy_decision_algos.py /^ def __call__(self, _, __):$/;" m class:BuyEverything file:
__call__ buy_decision_algos.py /^ def __call__(self, property_, player):$/;" m class:BuyIfNoOneOwnsTypeAndIsOfTheOneTypeOwned file:
__call__ buy_decision_algos.py /^ def __call__(self, property_, player):$/;" m class:BuyIfOwnFewerThanFivePropertiesOrHaveOneOfThisColor file:
__call__ buy_decision_algos.py /^ def __call__(self, property_: "Property", player: "Player"):$/;" m class:BuyDecision file:
__call__ buy_decision_algos.py /^ def __call__(self, property_: "Property", player: "Player"):$/;" m class:BuyIfDontHaveTwoPartialMonopoliesOfOtherColors file:
__call__ buy_decision_algos.py /^ def __call__(self, property_: "Property", player: "Player"):$/;" m class:BuyIfHaveThreeTimesPrice file:
__init__ monopoly.py /^ def __init__($/;" m class:BuildableProperty
__init__ monopoly.py /^ def __init__(self):$/;" m class:Player
__init__ monopoly.py /^ def __init__(self, _name):$/;" m class:Property
__init__ monopoly.py /^ def __init__(self, num_players, buy_decision_algorithm, slow_down=False):$/;" m class:Game
__init__ monopoly.py /^ def __init__(self, player: "Player"):$/;" m class:GetOutOfJailDecision
__init__ monopoly.py /^ def __init__(self, property_: "BuildableProperty"):$/;" m class:Monopoly
__repr__ monopoly.py /^ def __repr__(self):$/;" m class:Card file:
__repr__ monopoly.py /^ def __repr__(self):$/;" m class:Monopoly file:
__repr__ monopoly.py /^ def __repr__(self):$/;" m class:Property file:
__repr__ monopoly.py /^ def __repr__(self):$/;" m class:Space file:
__str__ monopoly.py /^ def __str__(self):$/;" m class:Player file:
_name monopoly.py /^ _name={"français": "Aarau Rathausplatz"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Berne Place Fédérale"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Berne Spitalgasse"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Bienne Rue De Nidau"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Bâle Freie Strasse"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Bâle Steinen-Vorstadt"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Coire Kornplatz"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Fribourg Avenue de la Gare"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Genève Rue de la Croix-D'Or"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "La Chaux-de-Fonds Avenue Louis-Robert"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Lausanne Place St. François"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Lausanne Rue de Bourg"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Lucerne Weggisgasse"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Lugano Via Nassa"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Neuchâtel Place Pury"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Schaffhouse Vordergasse"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Soleure Hauptgasse"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "St-Gall Markplatz"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Thoune Hauptgasse"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Winterthour Bahnhofplatz"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Zurich Paradeplatz"},$/;" v class:Board
_name monopoly.py /^ _name={"français": "Zurich Rennweg"},$/;" v class:Board
_name monopoly.py /^ _name = {"français": "Chance", "deutsch": "Chance", "English": "Chance"}$/;" v class:Chance
_name monopoly.py /^ _name = {"français": "Chancellerie", "deutsch": "Kanzlei"}$/;" v class:CommunityChest
_name monopoly.py /^ _name = {"français": "Impôt Supplémentaire", "deutsch": "Nachsteuer"}$/;" v class:LuxuryTax
action monopoly.py /^ def action(cls, player, _):$/;" m class:AdvanceCard
action monopoly.py /^ def action(cls, player, _):$/;" m class:CardSpace
action monopoly.py /^ def action(cls, player, _):$/;" m class:ElectedPresidentCard
action monopoly.py /^ def action(cls, player, _):$/;" m class:GoToJail
action monopoly.py /^ def action(cls, player, _):$/;" m class:RepairPropertyCard
action monopoly.py /^ def action(cls, player, _):$/;" m class:SpeedingCard
action monopoly.py /^ def action(cls, player, _):$/;" m class:TaxSpace
action monopoly.py /^ def action(cls, player: "Player", _):$/;" m class:GetOutOfJailFreeCard
action monopoly.py /^ def action(self, player, _):$/;" m class:BuildingAndLoanMaturesCard
action monopoly.py /^ def action(self, player: "Player", _):$/;" m class:Card
action monopoly.py /^ def action(self, player: "Player", _):$/;" m class:NothingHappensWhenYouLandOnItSpace
action monopoly.py /^ def action(self, player: "Player", last_roll=None):$/;" m class:Property
active_players monopoly.py /^ def active_players(self):$/;" m class:Game
advance monopoly.py /^ def advance($/;" m class:Player
amount monopoly.py /^ amount = 100$/;" v class:IncomeTax
amount monopoly.py /^ amount = 75$/;" v class:LuxuryTax
amount monopoly.py /^ amount = None$/;" v class:TaxSpace
assets monopoly.py /^ def assets(self):$/;" m class:Player
bankrupt monopoly.py /^ bankrupt = False$/;" v class:Player
buildable_properties monopoly.py /^ def buildable_properties(self) -> List[BuildableProperty]:$/;" m class:Player
buy monopoly.py /^ def buy(self, property_: "Property", from_=Bank, cost=None):$/;" m class:Player
buy_building monopoly.py /^ def buy_building(self, building_type):$/;" m class:BuildableProperty
buy_buildings_if_possible monopoly.py /^ def buy_buildings_if_possible(self):$/;" m class:Player
buy_decision monopoly.py /^def buy_decision(property: "Property", player: "Player"):$/;" f
calculate_rent monopoly.py /^ def calculate_rent(self, _):$/;" m class:BuildableProperty
calculate_rent monopoly.py /^ def calculate_rent(self, _):$/;" m class:Property
calculate_rent monopoly.py /^ def calculate_rent(self, _):$/;" m class:Railroad
calculate_rent monopoly.py /^ def calculate_rent(self, last_roll: int):$/;" m class:Utility
can_afford monopoly.py /^ def can_afford(self, cost):$/;" m class:Player
check_args monopoly.py /^def check_args(num_spaces, space_index, until_space_type):$/;" f
check_building_type monopoly.py /^ def check_building_type(type_):$/;" m class:Bank
check_funds monopoly.py /^ def check_funds(self, amount):$/;" m class:Player
color monopoly.py /^ color="brown",$/;" v class:Board
color monopoly.py /^ color="dark blue",$/;" v class:Board
color monopoly.py /^ color="green",$/;" v class:Board
color monopoly.py /^ color="light blue",$/;" v class:Board
color monopoly.py /^ color="orange",$/;" v class:Board
color monopoly.py /^ color="pink",$/;" v class:Board
color monopoly.py /^ color="red",$/;" v class:Board
color monopoly.py /^ color="yellow",$/;" v class:Board
cost monopoly.py /^ cost=100,$/;" v class:Board
cost monopoly.py /^ cost=120,$/;" v class:Board
cost monopoly.py /^ cost=140,$/;" v class:Board
cost monopoly.py /^ cost=160,$/;" v class:Board
cost monopoly.py /^ cost=180,$/;" v class:Board
cost monopoly.py /^ cost=200,$/;" v class:Board
cost monopoly.py /^ cost=220,$/;" v class:Board
cost monopoly.py /^ cost=240,$/;" v class:Board
cost monopoly.py /^ cost=260,$/;" v class:Board
cost monopoly.py /^ cost=280,$/;" v class:Board
cost monopoly.py /^ cost=300,$/;" v class:Board
cost monopoly.py /^ cost=320,$/;" v class:Board
cost monopoly.py /^ cost=350,$/;" v class:Board
cost monopoly.py /^ cost=400,$/;" v class:Board
cost monopoly.py /^ cost=60,$/;" v class:Board
cost monopoly.py /^ cost = 0$/;" v class:Property
cost monopoly.py /^ cost = 200$/;" v class:Railroad
cost monopoly.py /^ cost = 200$/;" v class:Utility
cost monopoly.py /^ cost = None$/;" v class:Card
current_space_index monopoly.py /^ current_space_index = get_space_index("Go")$/;" v class:Player
deck monopoly.py /^ deck = "ChanceDeck"$/;" v class:Chance
deck monopoly.py /^ deck = "CommunityChestDeck"$/;" v class:CommunityChest
deck monopoly.py /^ deck = None$/;" v class:CardSpace
deck monopoly.py /^ deck = None$/;" v class:Deck
deck monopoly.py /^ deck = [$/;" v class:ChanceDeck
deck monopoly.py /^ deck = [GoToJailCard, SpeedingCard]$/;" v class:CommunityChestDeck
do_action_of_current_space monopoly.py /^ def do_action_of_current_space(self, last_roll=None):$/;" m class:Player
end monopoly.py /^ def end(self):$/;" m class:Game
game simulate.py /^ game = Game(num_players_, buy_decision_algorithm=buy_decision_algorithm, slow_down=slow_down)$/;" v
games monopoly.py /^ games = []$/;" v class:Game
get_building monopoly.py /^ def get_building(cls, type_):$/;" m class:Bank
get_building_store monopoly.py /^ def get_building_store(cls, type_):$/;" m class:Bank
get_card monopoly.py /^ def get_card(cls):$/;" m class:Deck
get_index_of_next_space_of_type monopoly.py /^def get_index_of_next_space_of_type(current_space_index, until_space_type):$/;" f
get_num_of_type monopoly.py /^ def get_num_of_type(cls, type):$/;" m class:Property
get_out_of_jail_free_card monopoly.py /^ get_out_of_jail_free_card = False$/;" v class:Player
get_property_with_least_number_of_houses monopoly.py /^def get_property_with_least_number_of_houses(properties):$/;" f
get_property_with_no_hotels monopoly.py /^def get_property_with_no_hotels(properties):$/;" f
get_results simulate.py /^def get_results(results, game, attrs_to_get):$/;" f
get_rounds_played_per_player monopoly.py /^ def get_rounds_played_per_player(self):$/;" m class:Game
get_space_index monopoly.py /^def get_space_index(name):$/;" f
go_again monopoly.py /^ go_again = False$/;" v class:Player
go_to_jail monopoly.py /^ def go_to_jail(self):$/;" m class:Player
house_and_hotel_cost monopoly.py /^ house_and_hotel_cost=100,$/;" v class:Board
house_and_hotel_cost monopoly.py /^ house_and_hotel_cost=150,$/;" v class:Board
house_and_hotel_cost monopoly.py /^ house_and_hotel_cost=200,$/;" v class:Board
house_and_hotel_cost monopoly.py /^ house_and_hotel_cost=50,$/;" v class:Board
in_jail monopoly.py /^ in_jail = False$/;" v class:Player
instances monopoly.py /^ instances = []$/;" v class:Property
instances_by_type monopoly.py /^ def instances_by_type(cls):$/;" m class:Property
keep monopoly.py /^ keep = False$/;" v class:Card
kwarg monopoly.py /^ kwarg = {"num_spaces": 3}$/;" v class:AdvanceThreeSpacesCard
kwarg monopoly.py /^ kwarg = {"space_index": "Berne Place Fédérale"}$/;" v class:GoToBernPlaceFederaleCard
kwarg monopoly.py /^ kwarg = {"until_space_type": "Jail", "pass_go": False}$/;" v class:GoToJailCard
kwarg monopoly.py /^ kwarg = {"until_space_type": "Railroad"}$/;" v class:GoToClosestRailroadCard
kwarg monopoly.py /^ kwarg = {}$/;" v class:AdvanceCard
mandatory_action monopoly.py /^ mandatory_action = False$/;" v class:Card
mandatory_action monopoly.py /^ mandatory_action = True$/;" v class:AdvanceCard
mandatory_action monopoly.py /^ mandatory_action = True$/;" v class:AdvanceThreeSpacesCard
mandatory_action monopoly.py /^ mandatory_action = True$/;" v class:BuildingAndLoanMaturesCard
mandatory_action monopoly.py /^ mandatory_action = True$/;" v class:ElectedPresidentCard
mandatory_action monopoly.py /^ mandatory_action = True$/;" v class:GoToBernPlaceFederaleCard
mandatory_action monopoly.py /^ mandatory_action = True$/;" v class:SpeedingCard
money monopoly.py /^ money = 0$/;" v class:Player
money monopoly.py /^ money = ALL_MONEY$/;" v class:Bank
monopolies monopoly.py /^ monopolies = []$/;" v class:Player
mortgage monopoly.py /^ def mortgage(self, player: "Player"):$/;" m class:BuildableProperty
mortgage_cost monopoly.py /^ mortgage_cost=100,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=110,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=120,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=130,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=140,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=150,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=160,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=175,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=200,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=30,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=50,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=60,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=70,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=80,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost=90,$/;" v class:Board
mortgage_cost monopoly.py /^ mortgage_cost = 100$/;" v class:Railroad
mortgage_cost monopoly.py /^ mortgage_cost = 75$/;" v class:Utility
mortgaged monopoly.py /^ mortgaged = False$/;" v class:Property
next_building monopoly.py /^ def next_building(self) -> Tuple[Optional[str], Optional["BuildableProperty"]]:$/;" m class:Monopoly
num_hotels monopoly.py /^ def num_hotels(self):$/;" m class:Monopoly
num_houses monopoly.py /^ def num_houses(self):$/;" m class:Monopoly
num_of_type monopoly.py /^ def num_of_type(self):$/;" m class:Property
owner monopoly.py /^ owner = None$/;" v class:Property
owns_all_type monopoly.py /^ def owns_all_type(self, type_):$/;" m class:Player
owns_x_of_type monopoly.py /^ def owns_x_of_type(self, type_):$/;" m class:Player
passed_go_times monopoly.py /^ passed_go_times = 0$/;" v class:Player
pay monopoly.py /^ def pay(cls, actor: "EconomicActor", amount: int):$/;" m class:Bank
pay monopoly.py /^ def pay(self, actor: Type["EconomicActor"], amount: int):$/;" m class:Player
play_x_games simulate.py /^def play_x_games($/;" f
print_results simulate.py /^def print_results(results, num_players):$/;" f
properties monopoly.py /^ def properties(self) -> List["Property"]:$/;" m class:Player
properties_by_type monopoly.py /^ def properties_by_type(self):$/;" m class:Player
properties_of_type monopoly.py /^ def properties_of_type(self):$/;" m class:Property
put_building monopoly.py /^ def put_building(cls, type_, quantity):$/;" m class:Bank
rent monopoly.py /^ rent={$/;" v class:Board
rent monopoly.py /^ rent={0: 10, "monopoly": 20, 1: 50, 2: 150, 3: 450, 4: 625, "hotel": 750},$/;" v class:Board
rent monopoly.py /^ rent={0: 12, "monopoly": 24, 1: 60, 2: 180, 3: 500, 4: 700, "hotel": 900},$/;" v class:Board
rent monopoly.py /^ rent={0: 14, "monopoly": 28, 1: 70, 2: 200, 3: 550, 4: 750, "hotel": 950},$/;" v class:Board
rent monopoly.py /^ rent={0: 16, "monopoly": 32, 1: 80, 2: 220, 3: 600, 4: 800, "hotel": 1_000},$/;" v class:Board
rent monopoly.py /^ rent={0: 18, "monopoly": 39, 1: 90, 2: 250, 3: 700, 4: 875, "hotel": 1_050},$/;" v class:Board
rent monopoly.py /^ rent={0: 2, "monopoly": 4, 1: 10, 2: 30, 3: 90, 4: 160, "hotel": 250},$/;" v class:Board
rent monopoly.py /^ rent={0: 4, "monopoly": 8, 1: 20, 2: 60, 3: 180, 4: 320, "hotel": 450},$/;" v class:Board
rent monopoly.py /^ rent={0: 6, "monopoly": 12, 1: 30, 2: 90, 3: 270, 4: 400, "hotel": 550},$/;" v class:Board
rent monopoly.py /^ rent={0: 8, "monopoly": 16, 1: 30, 2: 100, 3: 300, 4: 400, "hotel": 600},$/;" v class:Board
rent monopoly.py /^ rent = {$/;" v class:Utility
rent monopoly.py /^ rent = {1: 25, 2: 50, 3: 100, 4: 200}$/;" v class:Railroad
reset monopoly.py /^ def reset(cls):$/;" m class:Bank
reset monopoly.py /^ def reset(cls):$/;" m class:Property
results simulate.py /^ results = defaultdict(list)$/;" v
roll_the_dice monopoly.py /^ def roll_the_dice() -> Tuple[int, Doubles]:$/;" m class:Player
rounds monopoly.py /^ rounds = 0$/;" v class:Game
sell_buildings monopoly.py /^ def sell_buildings(self, building_type, quantity):$/;" m class:BuildableProperty
shuffle monopoly.py /^ def shuffle(cls):$/;" m class:Deck
shuffle_decks monopoly.py /^def shuffle_decks():$/;" f
space_name monopoly.py /^ space_name = space.__name__$/;" v class:Board
space_name monopoly.py /^ space_name = space._name$/;" v class:Board
space_name monopoly.py /^ space_name = space._name.get(LANGUAGE) or space._name.get(BACKUP_LANGUAGE)$/;" v class:Board
spaces monopoly.py /^ spaces = [$/;" v class:Board
start monopoly.py /^ def start(self):$/;" m class:Game
take_a_turn monopoly.py /^ def take_a_turn(self):$/;" m class:Player
test_bank_reset tests/test_monopoly.py /^def test_bank_reset():$/;" f
test_property_reset tests/test_monopoly.py /^def test_property_reset():$/;" f
test_roll_the_dice tests/test_monopoly.py /^def test_roll_the_dice():$/;" f
text monopoly.py /^ text = {"français": "Amende pour excès de vitesse. Payez M15."}$/;" v class:SpeedingCard
text monopoly.py /^ text = {"français": "Avancez jusqu'à le chemin de fer le plus proche."}$/;" v class:GoToClosestRailroadCard
text monopoly.py /^ text = {"français": "Reculez de trois cases."}$/;" v class:AdvanceThreeSpacesCard
text monopoly.py /^ text = {$/;" v class:BuildingAndLoanMaturesCard
text monopoly.py /^ text = {$/;" v class:ElectedPresidentCard
text monopoly.py /^ text = {$/;" v class:GetOutOfJailFreeCard
text monopoly.py /^ text = {$/;" v class:GoToBernPlaceFederaleCard
text monopoly.py /^ text = {$/;" v class:GoToJailCard
text monopoly.py /^ text = {$/;" v class:RepairPropertyCard
total_property_mortgage_value monopoly.py /^ def total_property_mortgage_value(self):$/;" m class:Player
type monopoly.py /^ type = "railroad"$/;" v class:Railroad
type monopoly.py /^ type = "utility"$/;" v class:Utility
type monopoly.py /^ type = None$/;" v class:Property
un_mortgage monopoly.py /^ def un_mortgage(self, player: "Player"):$/;" m class:BuildableProperty
unmortgage_cost monopoly.py /^ unmortgage_cost=110,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=121,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=132,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=143,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=154,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=165,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=176,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=193,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=220,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=33,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=55,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=66,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=77,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=88,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost=99,$/;" v class:Board
unmortgage_cost monopoly.py /^ unmortgage_cost = 110$/;" v class:Railroad
unmortgage_cost monopoly.py /^ unmortgage_cost = 83$/;" v class:Utility

View File

21
tests/test_monopoly.py Normal file
View File

@@ -0,0 +1,21 @@
from monopoly import Bank, NUM_HOUSES, NUM_HOTELS, ALL_MONEY, Property, Player
import pytest
def test_bank_reset():
Bank.reset()
assert Bank.NUM_HOTELS == NUM_HOTELS
assert Bank.NUM_HOUSES == NUM_HOUSES
assert Bank.money == ALL_MONEY
def test_property_reset():
Property.reset()
assert all(p.owner is None for p in Property.instances)
def test_roll_the_dice():
for i in range(200):
num, doubles = Player.roll_the_dice()
assert 2 <= num <= 12
assert isinstance(doubles, bool)