Skip to content

Instantly share code, notes, and snippets.

@MariosRichards
Created October 26, 2024 14:46
Show Gist options
  • Save MariosRichards/1b7ded26a530ce961fc21c4dd6875f16 to your computer and use it in GitHub Desktop.
Save MariosRichards/1b7ded26a530ce961fc21c4dd6875f16 to your computer and use it in GitHub Desktop.
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dumb Model</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>
<div id="grid"></div>
<div>
<input name="updateButton"
type="button"
value="Update"
onclick="setup()" />
</div>
<!-- <script src="grid2.js" type="text/javascript"></script> -->
<script>
function gridData(granularity) {
var data = new Array();
var xpos = 1; //starting xpos and ypos at 1 so the stroke will show when we make the grid below
var ypos = 1;
for(var cell = 0; cell < granularity * granularity; cell++) {
data.push( {
id: cell,
color: '#ffffff',
xpos: cell % granularity,
ypos: Math.floor( cell / granularity),
support: 0,
} );
}
return data;
}
var window_x = 700;
var window_y = 700;
var granularity = 50;
var max_distance = (granularity/2)**2
//granularity ** 4;
var width = window_x/granularity;
var height = window_y/granularity;
var turn_delay = .001;
var gridData;
// gridData = gridData(granularity);
var partyData;
// = new Array();
// partyData.push( { xpos: 7, ypos: 12, color:'#597cf4', support:0, last_support:0, dx:0, dy:0 } );
// partyData.push( { xpos: 33, ypos: 46, color:'#f45962', support:0, last_support:0, dx:0, dy:0 } );
var party_turn=0;
var grid;
function setup(){
var data = new Array();
var xpos = 1; //starting xpos and ypos at 1 so the stroke will show when we make the grid below
var ypos = 1;
for(var cell = 0; cell < granularity * granularity; cell++) {
data.push( {
id: cell,
color: '#ffffff',
xpos: cell % granularity,
ypos: Math.floor( cell / granularity),
support: 0,
} );
}
gridData = data;
partyData = new Array();
partyData.push( { xpos: 7, ypos: 12, color:'#597cf4', support:0, last_support:0, dx:0, dy:0 } );
partyData.push( { xpos: 33, ypos: 46, color:'#f45962', support:0, last_support:0, dx:0, dy:0 } );
party_turn=0;
d3.select("svg").remove();
grid = d3.select("#grid")
// .append("svg")
.append("div")
// Container class to make it responsive.
.classed("svg-container", true)
.append("svg")
// Responsive SVG needs these 2 attributes and no width and height attr.
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 600 400")
// Class to make it responsive.
.classed("svg-content-responsive", true)
// Fill with a rectangle for visualization.
.attr("width",window_x.toString().concat("px"))
.attr("height",window_y.toString().concat("px"))
.on('click', function() {
var coords = d3.mouse(this);
partyData.push( { xpos: Math.floor(coords[0]*granularity/window_x), ypos: Math.floor(coords[1]*granularity/window_y), color:'#'+Math.floor(Math.random()*16777215).toString(16), support:0, last_support:0, dx:0,dy:0 } );
// drawCircle(coords[0], coords[1], getRandom(5,50));
});
}
setup();
// red = #f45962 , blue =#597cf4, white=#ffffff
// var grid = d3.select("#grid")
// .append("svg")
// .attr("width",window_x.toString().concat("px"))
// .attr("height",window_y.toString().concat("px"))
// .on('click', function() {
// var coords = d3.mouse(this);
// partyData.push( { xpos: Math.floor(coords[0]*granularity/window_x), ypos: Math.floor(coords[1]*granularity/window_y), color:'#'+Math.floor(Math.random()*16777215).toString(16), support:0, last_support:0, dx:0,dy:0 } );
// // drawCircle(coords[0], coords[1], getRandom(5,50));
// });
// Create data = list of groups
var allGroup = [".001", "1", "100", "1500"]
// Initialize the button
var dropdownButton = d3.select("#grid")
.append('select')
// add the options to the button
dropdownButton // Add a button
.selectAll('myOptions') // Next 4 lines add 6 options = 6 colors
.data(allGroup)
.enter()
.append('option')
.text(function (d) { return d; }) // text showed in the menu
.attr("value", function (d) { return d; }) // corresponding value returned by the button
// A function that update the color of the circle
function updateChart(mycolor) {
turn_delay = parseFloat(mycolor);
// zeCircle
// .transition()
// .duration(1000)
// .style("fill", mycolor)
}
// When the button is changed, run the updateChart function
dropdownButton.on("change", function(d) {
// recover the option that has been chosen
var selectedOption = d3.select(this).property("value")
// run the updateChart function with this selected option
updateChart(selectedOption)
})
function support_change(gridData, partyData, party_no, dx, dy){
var party = partyData[party_no];
party.xpos = party.xpos + dx;
party.ypos = party.ypos + dy;
if ( party.xpos<0 || party.xpos>=granularity ) {
party.xpos = party.xpos - dx;
party.ypos = party.ypos - dy;
return -1
}
if ( party.ypos<0 || party.ypos>=granularity ) {
party.xpos = party.xpos - dx;
party.ypos = party.ypos - dy;
return -1
}
for(var j = 0; j < partyData.length;j++){
var party = partyData[j];
// party.last_support = party.support;
party.support = 0;
if ( (j!=party_no) && (party.xpos == partyData[party_no].xpos) && (party.ypos == partyData[party_no].ypos) )
{
party.xpos = party.xpos - dx;
party.ypos = party.ypos - dy;
return -1
}
}
for(var i = 0; i < gridData.length;i++){
var cell = gridData[i];
var distance = max_distance;
for(var j = 0; j < partyData.length;j++){
var party = partyData[j];
var party_cell_dist = (party.xpos - cell.xpos)*(party.xpos - cell.xpos) + (party.ypos - cell.ypos)*(party.ypos - cell.ypos) ;
if (party_cell_dist<distance) {
distance = party_cell_dist;
// cell.color = party.color;
cell.support = j;
}
}
var party = partyData[cell.support];
party.support = party.support+1;
// cell.color = '#'+Math.floor(Math.random()*16777215).toString(16);
}
var party = partyData[party_no];
party.xpos = party.xpos - dx;
party.ypos = party.ypos - dy;
return party.support
}
function choose_move( move )
{
var dx,dy;
if (move==0) {dx = 0, dy = 1}
if (move==1) {dx = 1, dy = 0}
if (move==2) {dx = 0, dy = -1}
if (move==3) {dx = -1, dy = 0}
return [dx,dy]
}
function update(){
// move parties
// move only one party per update
// for(var j = 0; j < partyData.length;j++){
j = party_turn;
party_turn = (party_turn+1)%partyData.length;
var party = partyData[j];
// var acceptableMove = false;
// while (! acceptableMove)
// {
// acceptableMove=true;
// var move = Math.floor(Math.random()*4) // 0,1,2,3 compass directions
// if (move==0) {party.dx = 0, party.dy = 1}
// if (move==1) {party.dx = 1, party.dy = 0}
// if (move==2) {party.dx = 0, party.dy = -1}
// if (move==3) {party.dx = -1, party.dy = 0}
// if ( (party.xpos+party.dx)<=0 || (party.xpos+party.dx)>=granularity ) { acceptableMove=false }
// if ( (party.ypos+party.dy)<=0 || (party.ypos+party.dy)>=granularity ) { acceptableMove=false }
// }
// party.xpos = party.xpos + party.dx
// party.ypos = party.ypos + party.dy
var best_move = -1;
var best_support = party.support;
for(var move = 0; move <4; move++){
const [dx, dy] = choose_move(move);
// console.log(dx,dy)
var support = support_change(gridData, partyData, j, dx, dy)
if (support > best_support)
{
best_support = support;
best_move = move;
}
}
if (best_move >-1)
{
const [dx, dy] = choose_move(best_move);
// var party = partyData[party_no];
party.xpos = party.xpos + dx;
party.ypos = party.ypos + dy;
}
// if (Math.random()>.5) { party.xpos = party.xpos+1 }
// else { party.xpos = party.xpos-1 }
// if (Math.random()>.5) { party.ypos = party.ypos+1 }
// else { party.ypos = party.ypos-1 }
// party.xpos = (granularity+party.xpos)%granularity
// party.ypos = (granularity+party.ypos)%granularity
// }
for(var j = 0; j < partyData.length;j++){
var party = partyData[j];
// party.last_support = party.support;
party.support = 0;
}
// final update!
for(var i = 0; i < gridData.length;i++){
var cell = gridData[i];
var distance = max_distance;
cell.color = '#828282'; //default non-voting
for(var j = 0; j < partyData.length;j++){
var party = partyData[j];
var party_cell_dist = (party.xpos - cell.xpos)*(party.xpos - cell.xpos) + (party.ypos - cell.ypos)*(party.ypos - cell.ypos) ;
if (party_cell_dist<distance) {
distance = party_cell_dist;
cell.color = party.color;
cell.support = j;
}
}
var party = partyData[cell.support];
party.support = party.support+1;
}
var cell = grid.selectAll(".cell")
.data(gridData)
cell.enter().append("rect")
.attr("class","cell")
.attr("x", function(d) { return d.xpos * width; })
.attr("y", function(d) { return d.ypos * height; })
.attr("width", width )
.attr("height", height)
.style("fill", function(d){ return d.color; })
// .style("stroke", "#222");
cell.transition()
.duration(turn_delay)
.delay(turn_delay)
.style('fill',
function(d){
return d.color;
});
var party = grid.selectAll(".party")
.data(partyData)
party.enter().append("circle")
.attr("class","party")
.attr("cx", function(d) { return d.xpos * width; })
.attr("cy", function(d) { return d.ypos * height; })
.attr("r", width )
// .attr("width", width )
// .attr("height", height)
// .style("fill", function(d){ return d.color; })
// .style("fill", "#222")
.style("fill", "#ffffff")
.style("stroke", "#222")
// .append("text")
// .attr("x", function(d) { return d.xpos * width; })
// .attr("y", function(d) { return d.ypos * width; })
// .text(function(d){return d.support.toString()});
// circle
// .attr("cx", function(d) { d.x += d.dx; if (d.x > w) d.x -= w; else if (d.x < 0) d.x += w; return d.x; })
// .attr("cy", function(d) { d.y += d.dy; if (d.y > h) d.y -= h; else if (d.y < 0) d.y += h; return d.y; });
// svg.on('click', function() {
// var coords = d3.mouse(this);
// console.log(coords);
// drawCircle(coords[0], coords[1], getRandom(5,50));
// });
party.transition()
.duration(turn_delay)
.delay(turn_delay)
.attr("cx", function(d) { return d.xpos * width; })
.attr("cy", function(d) { return d.ypos * height; })
.style('fill',
function(d){
return d.color;
})
.attr("x", function(d) { return d.xpos * width; })
.attr("y", function(d) { return d.ypos * width; })
// .attr("dx", function(d){return -20})
// .append("text")
// .text(function(d){return d.support.toString()});
// .attr({
// "text-anchor": "middle",
// "font-size": function(d) {
// return d.r / ((d.r * 10) / 100);
// },
// "dy": function(d) {
// return d.r / ((d.r * 25) / 100);
// }
// });
// .selectAll('circle')
// party.enter()
// .append("text")
// .attr("x", function(d) { return d.xpos * width; })
// .attr("y", function(d) { return d.ypos * width; })
// .text(function(d){return d.support.toString()});
}
setInterval(update, turn_delay );
// Resources
// https://strongriley.github.io/d3/ex/voronoi.html
// https://www.visualcinnamon.com/2015/07/voronoi.html
// https://www.d3-graph-gallery.com/graph/heatmap_basic.html
// http://cs.tau.ac.il/~mfeldman/papers/FFOc16.pdf
// https://hashtagcolor.com/828282
// https://gist.github.com/MariosRichards/e2abce29f925fe22a6a8d7f1885026dc
</script>
<script src="https://cdn.jsdelivr.net/gh/sebt3/[email protected]/dist/d3-bootstrap-withextra.min.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment