JSFiddle - React, Tailwind, and code Playground

by Chris Maloney

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.4.2/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/d3-selection-multi.min.js"></script>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>repl.it</title>
    <link href="style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <div id='screen'></div>
    <script src="script.js"></script>
    <pre id='log'></pre>
  </body>
</html>

JavaScript

//options
const numPeople = 100
const recoveryTime = 3
const contactDistance = 15
const deathChancePerTick = 0.0025

const scr = d3.select('#screen')
const svg = scr.append('svg').attrs({
  width: 500,
  height: 500,
})
svg.append('rect').attrs({
  x: 0, y: 0,
  width: 500, height: 500,
  fill: '#EEE'
})

const makePerson = (x, y, dir) => {
  const person = svg.append('circle').attrs({
    cx: x,
    cy: y,
    r: 5,
    fill: 'blue',
  })
  return {
    x, y, dir,
    person,
    status: 'healthy',
    recovery: 0,
  }
}
const infect = person => {
  person.status = 'sick'
  person.recovery = recoveryTime * 25
  person.person.attr('fill', 'red')
}
const recover = person => {
  person.status = 'recovered'
  person.recovery = 0
  person.person.attr('fill', 'gray')
}
const kill = person => {
  person.status = 'dead'
  person.person.attr('fill', 'none')
  person.person.attr('stroke', 'black')
}

const population = Array(numPeople)
  for (i = 0; i < numPeople; i++) {
  population[i] = makePerson(Math.random() * 490 + 5, Math.random() * 490 + 5, Math.random() * 2 * Math.PI)
}

const contact = (p0, p1) => {
  if (Math.abs(p1.x - p0.x) > contactDistance ||
      Math.abs(p1.y - p0.y) > contactDistance) 
    return false
  const dist = Math.sqrt(
    (p1.x - p0.x) ** 2 + (p1.y - p0.y) ** 2)
  return dist <= contactDistance
}

const update = () => {
  population.forEach(person => {
    if (person.status != 'dead') {
      person.x += Math.cos(person.dir) * 3
      person.y += Math.sin(person.dir) * 3
      if (person.x <= 5) {
        person.x = 6
        person.dir = Math.PI - person.dir
      }
      if (person.x >= 495) {
        person.x = 494
        person.dir = Math.PI - person.dir
      }
      if (person.y <= 5) {
        person.y = 6
        person.dir = 2 * Math.PI - person.dir
      }
      if (person.y >= 495) {
        person.y = 494
        person.dir = 2 * Math.PI - person.dir
      }
      person.person.attrs({
        cx: person.x,
        cy: person.y,
 ...