Skip to content

Commit e0bb4ff

Browse files
Rishav159redblobgames
authored andcommitted
Ch 6 - Add map coloring problem (map of Australia)
1 parent b6fba02 commit e0bb4ff

File tree

7 files changed

+357
-9
lines changed

7 files changed

+357
-9
lines changed

4-Beyond-Classical-Search/index.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<body>
3232

3333
<div class="row">
34-
<div class="col-sm-6 col-md-8 col-md-offset-1" id="content">
34+
<div class="col-sm-6 col-md-10 col-md-offset-1" id="content">
3535
<h1>Beyond classical search</h1>
3636

3737
<h2>Optimization Problem</h2>
@@ -230,7 +230,6 @@ <h4>Third Party License Information</h4>
230230

231231
</div>
232232
</div>
233-
</div>
234233
</body>
235234

236235
</html>
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
class AustraliaMapDiagram {
2+
constructor(selector, h, w) {
3+
this.w = w;
4+
this.h = h;
5+
this.root = selector;
6+
this.territories = [
7+
['WA','#WA',1/16,1/4],
8+
['NT','#NT',1/7.5,1/6],
9+
['SA','#SA',1/7,1/3.5],
10+
['Q','#Q',1/4.5,1/4.5],
11+
['NSW','#NSW',1/4.5,1/3],
12+
['V','#V',1/5,1/2.45],
13+
['T','#T',1/4.4,1/2]
14+
];
15+
this.colors = [
16+
['nc'],
17+
['r','.r',this.h/50,this.w/4],
18+
['g','.g',this.h/50,this.w/4+30],
19+
['b','.b',this.h/50,this.w/4+60]
20+
];
21+
}
22+
23+
init(problem) {
24+
this.problem = problem;
25+
this.activeColor = 'nc';
26+
this.drawText();
27+
this.bindClicks();
28+
this.drawPalette();
29+
this.drawStatus();
30+
}
31+
32+
drawStatus() {
33+
this.status = this.root.select('#states')
34+
.append('g')
35+
.classed('status', true);
36+
this.statusText = this.status.append('text')
37+
.attr('y', this.h / 2.2)
38+
.attr('x', this.w / 50)
39+
.text('7 uncolored states');
40+
}
41+
42+
updateStatus() {
43+
let unassigned = this.problem.countUnassigned();
44+
if (unassigned > 0) {
45+
this.statusText.attr('fill', 'black').text(`${unassigned} uncolored states`);
46+
} else {
47+
this.statusText.attr('fill', 'green').text(`Correct Assignment \u2714`);
48+
}
49+
}
50+
51+
checkConsistency() {
52+
//Remove any previous highlights for inconsistencies
53+
this.root.selectAll('.inconsistent-state').classed('inconsistent-state', false);
54+
let result = this.problem.checkConsistency();
55+
//If not consistent, highlight the inconsistent states.
56+
if (!result.consistent) {
57+
for (let i = 0; i < result.inconsistencies.length; i++) {
58+
this.root.select(`#${result.inconsistencies[i][0]}`).classed('inconsistent-state', true);
59+
this.root.select(`#${result.inconsistencies[i][1]}`).classed('inconsistent-state', true);
60+
}
61+
this.statusText.attr('fill', 'red').text('Inconsistent \u2718');
62+
} else {
63+
this.updateStatus();
64+
}
65+
}
66+
67+
drawPalette() {
68+
this.palette = this.root.select('#states')
69+
.append('g')
70+
.classed('palette', true);
71+
for(let i = 1; i < this.colors.length; i++) {
72+
let color = this.colors[i];
73+
this.palette
74+
.append('circle')
75+
.attr('r', 10)
76+
.attr('cy', color[2])
77+
.attr('cx', color[3])
78+
.classed('palette-color', true)
79+
.classed(color[0], true)
80+
.classed('clickable', true)
81+
.on('mousedown', () => {
82+
this.palette.selectAll('.active-palette').classed('active-palette',false);
83+
this.palette.select(color[1]).classed('active-palette',true);
84+
this.activeColor = color[0];
85+
});
86+
}
87+
}
88+
89+
removeColor(state) {
90+
this.root.select(`#${state}`)
91+
.classed('nc', false)
92+
.classed('r', false)
93+
.classed('g', false)
94+
.classed('b', false)
95+
}
96+
97+
colorState(state) {
98+
//Remove previous colors
99+
this.removeColor(state);
100+
this.root.select(`#${state}`)
101+
.classed(this.activeColor, true);
102+
this.problem.assign(state, this.activeColor);
103+
this.checkConsistency();
104+
}
105+
106+
bindClicks() {
107+
for(let i = 0; i < this.territories.length; i++) {
108+
let territory = this.territories[i];
109+
this.root.select(territory[1]).classed('clickable', true).on('mousedown', () => {
110+
this.colorState(territory[0]);
111+
});
112+
}
113+
}
114+
115+
drawText() {
116+
for(let i = 0; i < this.territories.length; i++) {
117+
let territory = this.territories[i];
118+
this.root.select(territory[1])
119+
120+
.append('text')
121+
.classed('territory-text',true)
122+
.attr('x', this.w *territory[2])
123+
.attr('y', this.h *territory[3])
124+
.text(territory[0]);
125+
}
126+
}
127+
}
128+
129+
$(document).ready(function() {
130+
function init() {
131+
//Load external SVG async
132+
d3.xml('../third-party/australia.svg', (xml) => {
133+
$('#mapColoring .canvas').html(xml.documentElement);
134+
var australiaMapDiagram = new AustraliaMapDiagram(d3.select('#mapColoring').select('.canvas'), 500, 1000);
135+
australiaMapColoringProblem.emptyAssignment();
136+
australiaMapDiagram.init(australiaMapColoringProblem);
137+
})
138+
}
139+
init();
140+
$('#mapColoring .restart-button').click(init);
141+
});
Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,55 @@
11
<!DOCTYPE html>
22
<html lang="en">
3+
34
<head>
45
<title>6 Constraint Satisfaction Problems</title>
56
<link rel="stylesheet" href="../styles.css">
7+
<link rel="stylesheet" href="main.css">
68
<script src="http://code.jquery.com/jquery-1.12.4.js"></script>
79
<script type="text/javascript" src="../main.js"></script>
8-
10+
911
<script src="https://cdnjs.cloudflare.com/ajax/libs/two.js/0.6.0/two.min.js"></script>
12+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
1013
<script type="text/javascript" src="./AC3.js"></script>
1114
<script type="text/javascript" src="./backtracking.js"></script>
1215
<script type="text/javascript" src="./minConflicts.js"></script>
1316
<script type="text/javascript" src="./treeCSP.js"></script>
17+
<script type="text/javascript" src="./mapColoring.js"></script>
18+
1419
<script type="text/javascript" src="./c_AC3.js"></script>
1520
<script type="text/javascript" src="./c_backtracking.js"></script>
1621
<script type="text/javascript" src="./c_minConflicts.js"></script>
1722
<script type="text/javascript" src="./c_treeCSP.js"></script>
23+
<script type="text/javascript" src="./c_mapColoring.js"></script>
1824
</head>
25+
1926
<body>
2027

2128
<div class="row">
22-
<div class="col-sm-6 col-md-offset-3" id="content">
29+
<div class="col-sm-8 col-md-10 col-md-offset-1" id="content">
2330
<h1>Constraint Satisfaction Problems</h1>
31+
<h2>Defining CSP with Map Coloring Problem</h2>
32+
<p>
33+
A map coloring problem is a type of CSP where each state can be assigned a color from the set (red,green,blue). The constraint involved says that no two neighbouring state is allowed to have the same color.
34+
</p>
35+
<p>
36+
Given below is a map of Australia showing its states and territories. You can select a color from the color palette given on the top right and then click on any state to color it with the selected color.
37+
</p>
38+
<p>
39+
Try to color all the states while satisfying the condition that neighbouring states cannot have the same color.
40+
</p>
41+
<div id='mapColoring'>
42+
<div class="row">
43+
<div class='btn btn-primary restart-button'>Restart</div>
44+
</div>
45+
<div class="row">
46+
<div class="canvas">
47+
48+
</div>
49+
</div>
50+
</div>
51+
52+
2453

2554
<h2>Arc consistency</h2>
2655
<p>
@@ -32,30 +61,38 @@ <h2>Arc consistency</h2>
3261
</ul>
3362
</p>
3463
<p>Step by step visualization of the procedure. Click to restart the simulation.</p>
35-
<div class = "canvas" id="ac3Canvas" height="300px">
64+
<div class="canvas" id="ac3Canvas" height="300px">
3665
</div>
3766
<pre id="AC3Code"></pre>
3867

3968
<h2>Backtracking</h2>
4069
<p>Map colouring using backtracking. Click to restart the simulation.</p>
41-
<div class = "canvas" id="backtrackCanvas" height="300px">
70+
<div class="canvas" id="backtrackCanvas" height="300px">
4271
</div>
4372
<pre id="backtrackingCode"></pre>
4473
<h2>Min Conflicts</h2>
4574
<p>Min conflicts stuff</p>
46-
<div class = "canvas" id="minConflictsCanvas" height="300px">
75+
<div class="canvas" id="minConflictsCanvas" height="300px">
4776
</div>
4877
<pre id="minConflictsCode"></pre>
4978

5079
<h2>Tree CSP</h2>
5180
<p>Tree CSP Stuff</p>
52-
<div class = "canvas" id="treeCspCanvas" height="300px">
81+
<div class="canvas" id="treeCspCanvas" height="300px">
5382
</div>
5483
<pre id="treeCSPCode"></pre>
5584

85+
<div class='license'>
86+
<h4>Third Party License Information</h4>
87+
<ul>
88+
<li>Australia Map Lokal_Profil [<a href="http://creativecommons.org/licenses/by-sa/2.5">CC BY-SA 2.5</a>, <a href="http://www.gnu.org/copyleft/fdl.html">GFDL</a> or <a href="http://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA-3.0</a>], <a href="https://commons.wikimedia.org/wiki/File%3AAustralia_map%2C_States.svg">via Wikimedia Commons</a></li>
89+
</ul>
90+
</div>
91+
5692
</div>
5793
</div>
5894

59-
</div>
95+
</div>
6096
</body>
97+
6198
</html>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.clickable {
2+
cursor: pointer;
3+
}
4+
5+
/*nc -> No color*/
6+
.territory-text{
7+
stroke:none;
8+
fill:black;
9+
}
10+
svg .nc {
11+
fill: hsl(0, 0%, 83%);
12+
}
13+
svg .r {
14+
fill: hsl(20, 80%, 70%);
15+
}
16+
svg .g {
17+
fill: hsl(110, 80%, 70%);
18+
}
19+
svg .b {
20+
fill: hsl(250, 80%, 70%);
21+
}
22+
.palette-color {
23+
opacity: 0.4;
24+
}
25+
.active-palette {
26+
opacity: 1;
27+
}
28+
.inconsistent-state {
29+
stroke: hsl(20, 100%, 50%);
30+
stroke-width: 1.5;
31+
}
32+
.status {
33+
fill: black;
34+
stroke: none;
35+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
class mapColoringProblem {
2+
constructor(variables, domains, neighbours, condition) {
3+
this.variables = variables;
4+
this.domains = domains;
5+
this.neighbours = neighbours;
6+
this.condition = condition;
7+
this.assignment = {};
8+
this.emptyAssignment();
9+
}
10+
//Assigns value 'a' to variable 'A'
11+
assign(A, a) {
12+
//Check if given value is in domain
13+
let domain = this.domains[A];
14+
if (domain.includes(a)) {
15+
this.assignment[A] = a;
16+
return true;
17+
} else {
18+
return false;
19+
}
20+
}
21+
22+
emptyAssignment() {
23+
for (let i = 0; i < this.variables.length; i++) {
24+
//'NIL' represents No assignment
25+
this.assignment[this.variables[i]] = 'nc';
26+
}
27+
}
28+
29+
countUnassigned() {
30+
let answer = 0;
31+
for (let i = 0; i < this.variables.length; i++) {
32+
//'NIL' represents No assignment
33+
if (this.assignment[this.variables[i]] == 'nc') {
34+
answer++;
35+
};
36+
}
37+
return answer;
38+
}
39+
40+
checkConsistency() {
41+
//'consistence' is true if the current assignment is correct
42+
//inconsistencies contains the list of neighbours that violate the condition
43+
let answer = {
44+
'consistent': true,
45+
'inconsistencies': []
46+
}
47+
48+
for (let i = 0; i < this.variables.length; i++) {
49+
let variable = this.variables[i];
50+
for (let j = 0; j < this.neighbours[variable].length; j++) {
51+
let neighbour = this.neighbours[variable][j];
52+
if (!this.condition(variable, this.assignment[variable], neighbour, this.assignment[neighbour])) {
53+
answer['consistent'] = false;
54+
answer['inconsistencies'].push([variable, neighbour]);
55+
}
56+
}
57+
}
58+
return answer;
59+
}
60+
61+
}
62+
63+
//Map Coloring Problem for Australia
64+
var australiaMapColoringProblem = new mapColoringProblem(
65+
["WA", "NT", "SA", "Q", "NSW", "V", "T"], {
66+
"WA": ["r", "g", "b"],
67+
"NT": ["r", "g", "b"],
68+
"SA": ["r", "g", "b"],
69+
"Q": ["r", "g", "b"],
70+
"NSW": ["r", "g", "b"],
71+
"V": ["r", "g", "b"],
72+
"T": ["r", "g", "b"]
73+
}, {
74+
"WA": ["NT", "SA"],
75+
"NT": ["WA", "SA", "Q"],
76+
"SA": ["WA", "NT", "Q", "NSW", "V"],
77+
"Q": ["NT", "SA", "NSW"],
78+
"NSW": ["Q", "SA", "V"],
79+
"V": ["SA", "NSW"],
80+
"T": []
81+
},
82+
function(A, a, B, b) {
83+
//If no color
84+
if (a == 'nc' || b == 'nc') {
85+
return true;
86+
}
87+
return a != b;
88+
});

0 commit comments

Comments
 (0)