paiza.ioでelixir その212

# 概要
paiza.ioでelixirやってみた。
練習問題やってみた。

# 練習問題
ハノイの塔を可視化せよ。

# 参考にしたページ

https://qiita.com/kob58im/items/eb30f4f06dafacb7ae6b


# サンプルコード

```

defmodule Stack do
	def start_link do
		Agent.start_link(fn ->
			[]
		end, name: __MODULE__)
	end
	def drop do
		Agent.update(__MODULE__, fn stack ->
		    po = length(stack) - 2
			stack = List.delete_at(stack, po + 1)
		end)
	end
	def get do
		Agent.get(__MODULE__, fn stack ->
			stack
		end)
	end
	def rev do
		Agent.get(__MODULE__, fn stack ->
			Enum.reverse(stack)
		end)
	end
	def push(v) do
		Agent.update(__MODULE__, fn stack ->
			stack ++ [v]
		end)
	end
end



defmodule Hanoi do
  def hanoi(n, a, b, c) do
    if n > 0 do
      hanoi n - 1, a, c, b
      Stack.push("move(#{a}, #{b});")
      hanoi n - 1, c, b, a
    end
  end
end

Stack.start_link
Hanoi.hanoi 4, "0", "1", "2"
IO.puts """
<!doctype html>

<html>
  <head>
    <link rel="stylesheet" href="lib/style.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
  </head>
  <body>
<canvas id="myCanvas" width="400" height="400"></canvas>
<script>
let dragStartX;
let dragStartY;
let dragStarted = false;
let currentAngleX = 3 * Math.PI / 4;
let currentAngleY = Math.PI / 7;
let oldAngle = -100000; 
const width = 400;
const height = 400;
const N = 4;
const holeR = 22;
const poleR = 13;
const thickness = 35;
const minR = 40;
const stepR = 22;
const poleHeight = thickness * (N + 4);
const distX = 1000 / 4;
const distZ = 1000 / 4;
const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector('#myCanvas')
});
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(-680, +680, 680, -680, 1, 1500);
window.addEventListener('load', init);
const canvas = document.getElementById("myCanvas");
canvas.addEventListener('mousedown', onMouseDown, false);
canvas.addEventListener('mousemove', onMouseMove, false);
canvas.addEventListener('mouseup'  , onMouseUp  , false);
canvas.addEventListener('mouseover', onMouseOver, false);
let rings = [];
let instructions = [];
let ringInPoles = [];
function makeRingGeometry(radius, holeRadius, extrude) {
  let arcShape = new THREE.Shape();
  arcShape.moveTo(0, 0);
  arcShape.arc(0, 0, radius, 0, Math.PI * 2, false);
  if (holeRadius > 0) 
  {
    let holePath = new THREE.Path();
    holePath.moveTo(0, 0);
    holePath.arc(0, 0, holeRadius, 0, Math.PI * 2, true);
    arcShape.holes.push(holePath);
  }
  let geometry = new THREE.ExtrudeGeometry(arcShape, extrude);
  let axis = new THREE.Vector3(1, 0, 0).normalize();
  let angle = Math.PI / 2;
  geometry.applyMatrix(new THREE.Matrix4().makeRotationAxis(axis, angle));
  return geometry;
}
function init() {
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);
  camera.position.set(0, 0, +800);
  const light = new THREE.HemisphereLight(0xdddddd, 0x555555, 1.0);
  scene.add(light);
  let extrudeSettings = { 
    amount: thickness, 
    bevelEnabled: true, 
    bevelSegments: 2, 
    steps: 2 
  };
  let extrudeSettingsPole = { 
    amount: poleHeight, 
    bevelEnabled: true, 
    bevelSegments: 2, 
    steps: 2 
  };
  let material = new THREE.MeshLambertMaterial({ 
    color: 0x7777ff,
    side: THREE.DoubleSide 
  });
  let materialLast = new THREE.MeshLambertMaterial({ 
    color: 0xcccccc,
    side: THREE.DoubleSide 
  });
  let materialPole = new THREE.MeshLambertMaterial({ 
    color: 0xcc3333,
    side: THREE.DoubleSide 
  });
  ringInPoles=[[], [], []];
  for (let i = 0; i < N; i++)
  {
    let geom = makeRingGeometry(minR + i * stepR, holeR, extrudeSettings);
    let mat = new THREE.Matrix4().makeTranslation(-distX, (N - 1 - i + 1) * thickness, -distZ);
    geom.applyMatrix(mat);
    if (i === N - 1)
    {
      rings.push(new THREE.Mesh(geom, materialLast));
    }
    else 
    {
      rings.push(new THREE.Mesh(geom, material));
    }
    ringInPoles[0].push(N - 1 - i);
    scene.add(rings[i]);
  }
  let geomPole;
  let mat;
  geomPole = makeRingGeometry(poleR, 0, extrudeSettingsPole);
  mat = new THREE.Matrix4().makeTranslation(-distX, poleHeight, -distZ);
  geomPole.applyMatrix(mat);
  scene.add(new THREE.Mesh(geomPole, materialPole));
  geomPole = makeRingGeometry(poleR, 0, extrudeSettingsPole);
  mat = new THREE.Matrix4().makeTranslation(0, poleHeight, 0);
  geomPole.applyMatrix(mat);
  scene.add(new THREE.Mesh(geomPole, materialPole));
  geomPole = makeRingGeometry(poleR, 0, extrudeSettingsPole);
  mat = new THREE.Matrix4().makeTranslation(distX, poleHeight, distZ);
  geomPole.applyMatrix(mat);
  scene.add(new THREE.Mesh(geomPole, materialPole));
  let gridHelper = new THREE.GridHelper(1000, 8);
  scene.add(gridHelper); 
  updateCameraPos();
  redraw();
"""
Enum.map(Stack.rev, fn v ->
    IO.puts v
end)
IO.puts """
  setInterval(timerFunc, 166);
}
function redraw() {
  renderer.render(scene, camera);
}
function move(pole1, pole2) {
  instructions.push(["transport", pole1, pole2]);
}
function fetchInstruction() {
  if (instructions.length === 0)
  {
    return 0;
  }
  let inst = instructions.pop();
  if (inst[0] == "transport") 
  {
    let poleFrom = inst[1];
    let poleTo = inst[2];
    let ringsFrom = ringInPoles[poleFrom];
    let ringsTo = ringInPoles[poleTo];
    let distanceToUp = poleHeight - ringInPoles[poleFrom].length * thickness + thickness * 2;
    let distanceToDown = poleHeight - ringInPoles[poleTo].length * thickness + thickness * (2 - 1);
    let distanceHorizontal = distX * (poleTo - poleFrom);
    let ringI = ringsFrom.pop();
    ringsTo.push(ringI);
    for (let i = 0; i < distanceToDown / thickness; i++)
    {
      instructions.push(["moveY", ringI, -thickness]);
    }
    for (let i = 0; i < Math.abs(distanceHorizontal) / 50; i++)
    {
      instructions.push(["moveXZ", ringI, Math.sign(distanceHorizontal) * 50]);
    }
    for (let i = 0; i < distanceToUp / thickness; i++)
    {
      instructions.push(["moveY", ringI, thickness]);
    }
    return fetchInstruction();
  }
  else if (inst[0] == "moveY")
  {
    let ringI = inst[1];
    let moveY = inst[2];
    mat = new THREE.Matrix4().makeTranslation(0, moveY, 0);
    rings[ringI].geometry.applyMatrix(mat);
    return instructions.length;
  }
  else if (inst[0] == "moveXZ")
  {
    let ringI = inst[1];
    let moveXZ = inst[2];
    mat = new THREE.Matrix4().makeTranslation(moveXZ, 0, moveXZ);
    rings[ringI].geometry.applyMatrix(mat);
    return instructions.length;
  }
  return 0;
}
function timerFunc() {
  fetchInstruction();
  redraw();
}
function onMouseDown(e) {
  let rect = e.target.getBoundingClientRect();
  let x = e.clientX - rect.left;
  let y = e.clientY - rect.top;
  dragStartX = x;
  dragStartY = y;
  dragStarted = true;
}
function updateCameraPos() {
  camera.position.x = 900 * Math.cos(currentAngleY) * (Math.cos(currentAngleX));
  camera.position.y = 900 * Math.sin(currentAngleY);
  camera.position.z = 900 * Math.cos(currentAngleY) * (Math.sin(currentAngleX));
  camera.lookAt(new THREE.Vector3(0, 100, 0));
}
function onMouseMove(e) {
  let rect = e.target.getBoundingClientRect();
  let x = e.clientX - rect.left;
  let y = e.clientY - rect.top;
  if (dragStarted) 
  {
    let dx = x - dragStartX;
    let dy = y - dragStartY;
    currentAngleX += dx / 50;
    currentAngleY += dy / 50;
    if (currentAngleY > 0.45 * Math.PI) 
    {
      currentAngleY = 0.45 * Math.PI;
    }
    else if (currentAngleY < -0.45 * Math.PI)
    {
      currentAngleY = -0.45 * Math.PI;
    }
    dragStartX = x;
    dragStartY = y;
    updateCameraPos();
    redraw();
  }
}
function onMouseUp(e) {
  dragStarted = false;
}
function onMouseOver(e) {
  dragStarted = false;
}
</script>
  </body>
</html>
"""









```

# 実行結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/c22e6e18-5077-0969-5f01-ecd0c53a0dad.png)


# 成果物

https://paiza.io/projects/QrIWVQgiOKxVz71j2BSeag?language=elixir


以上。