Skip to content
This repository has been archived by the owner on Oct 30, 2023. It is now read-only.

Latest commit

 

History

History
857 lines (723 loc) · 42.7 KB

karel_dreams.livemd

File metadata and controls

857 lines (723 loc) · 42.7 KB

Karel Dreams

Mix.install([
  {:kino, "~> 0.11.0"},
  {:jason, "~> 1.4"},
  {:langchain, "~> 0.1.2"}
])

Kino.nothing()

Images

defmodule Karel.Images do
  def robot do
    Map.new([:south, :north, :west, :east], &{&1, robot(&1)})
  end

  defp robot(:south) do
    """
    <svg viewBox="0 0 132.29166 132.29167" xmlns="http://www.w3.org/2000/svg" id="robot">
      <path
         id="karel-head"
         style="fill:#a87ace"
         d="m 40.011719,7.3203125 c -6.510773,0 -11.751953,5.2431345 -11.751953,11.7539065 v 29.927734 c 0,6.510772 5.24118,11.753906 11.751953,11.753906 H 92.28125 c 6.51077,0 11.75195,-5.243134 11.75195,-11.753906 V 19.074219 c 0,-6.510772 -5.24118,-11.7539065 -11.75195,-11.7539065 z M 22.660156,71.527344 c -4.721779,6.018212 -5.9597,15.605412 0.601563,27.521484 0.302747,-0.541869 0.678986,-1.037165 1.099609,-1.433594 0.55828,-0.52846 1.228262,-0.92105 1.966797,-1.177734 -5.607761,-10.584899 -4.207835,-17.837279 -0.566406,-22.478516 3.27477,-4.173907 8.72127,-6.197723 12.978515,-5.998046 0.343024,-1.508625 1.048892,-2.872595 2.025391,-4 -8.4347,-0.533794 -14.804313,2.897155 -18.105469,7.566406 z m 69.054688,-7.34961 c 0.881011,1.083258 1.516918,2.371721 1.83789,3.783204 4.256986,-0.199027 9.704206,1.824722 12.978516,5.998046 3.64165,4.641525 5.04062,11.894497 -0.56836,22.480469 0.73849,0.256704 1.40856,0.647326 1.9668,1.175781 0.44137,0.41598 0.80531,0.878502 1.09961,1.435547 6.56589,-11.915672 5.44475,-21.448189 0.72265,-27.466797 -4.9821,-5.450823 -9.33703,-7.952019 -18.037106,-7.40625 z M 49.597656,102.9668 v 9.3789 c -0.581674,2.30262 -2.038651,2.59961 -4.130859,2.59961 -2.134294,0 -3.853516,1.6844 -3.853516,3.77735 v 7.14453 c 0,2.09296 1.719222,3.77929 3.853516,3.77929 h 14.511719 c 2.134294,0 3.851562,-1.68633 3.851562,-3.77929 v -7.14453 c 0,-2.09295 -1.717268,-3.77735 -3.851562,-3.77735 -2.921841,0.14404 -3.944455,-1.01601 -4.210938,-2.59961 v -9.3789 z m 26.88086,0 v 9.3789 c -0.487963,2.20448 -2.057104,2.59961 -4.166016,2.59961 -2.134293,0 -3.851562,1.6844 -3.851562,3.77735 v 7.14453 c 0,2.09296 1.717269,3.77929 3.851562,3.77929 h 14.513672 c 2.134295,0 3.851562,-1.68633 3.851562,-3.77929 v -7.14453 c 0,-2.09295 -1.717267,-3.77735 -3.851562,-3.77735 -2.995071,0.048 -3.985629,-0.96906 -4.177734,-2.59961 v -9.3789 z" />
      <path
         id="karel-body"
         style="fill:#3dbcc0"
         d="m 54.52824,2.6458333 c -0.746052,0 -1.346688,0.600636 -1.346688,1.3466878 v 0.5105632 c 0,0.7460518 0.600636,1.3466878 1.346688,1.3466878 h 10.063468 v 1.471228 h 3.108337 V 5.8497721 H 77.76403 c 0.746053,0 1.346688,-0.600636 1.346688,-1.3466878 V 3.9925211 c 0,-0.7460518 -0.600635,-1.3466878 -1.346688,-1.3466878 z M 47.782915,60.754947 c -5.143282,0 -9.283671,4.140906 -9.283671,9.284188 v 23.643518 c 0,5.14328 4.140389,9.283667 9.283671,9.283667 h 36.725923 c 5.143282,0 9.284188,-4.140387 9.284188,-9.283667 V 70.039135 c 0,-5.143282 -4.140906,-9.284188 -9.284188,-9.284188 z M 27.321632,96.178103 c -1.139373,0.184091 -2.160416,0.679731 -2.960543,1.437121 -0.801756,0.755639 -1.45821,1.862159 -1.706946,2.989156 -0.15147,2.32877 0.06692,3.75461 1.44648,5.71691 0.448747,0.47506 1.18717,0.52567 1.696537,0.11628 2.473392,-2.33591 5.005226,-4.73113 7.291113,-6.886483 0.437856,-0.485308 0.429307,-1.225604 -0.01964,-1.700671 -1.784062,-1.336631 -3.101657,-1.92584 -5.747001,-1.672313 z m 72.096894,1.499138 0.004,0.0036 c -0.07225,0.04955 -0.139104,0.106529 -0.19947,0.170016 -0.448792,0.475208 -0.457106,1.215503 -0.0191,1.700671 2.505054,2.364212 4.957434,4.681672 7.290534,6.886482 0.50937,0.4094 1.24779,0.35879 1.69654,-0.11628 1.35233,-1.66223 1.70474,-4.24991 1.40879,-5.66553 -0.29596,-1.415622 -0.86761,-2.284897 -1.66937,-3.040535 -0.79999,-0.757307 -1.82085,-1.252942 -2.96003,-1.437121 -1.93945,-0.275856 -4.22188,0.534242 -5.551894,1.498697 z" />
      <path
         id="karel-k"
         style="fill:#ffffff"
         d="m 61.742687,85.892684 v 3.654422 h 1.191317 c 0.794205,0 1.357662,0.155623 1.690371,0.466868 0.343441,0.300508 0.515162,0.697611 0.515162,1.191308 0,0.482966 -0.171721,0.880069 -0.515162,1.191309 -0.332709,0.300513 -0.896166,0.45077 -1.690371,0.45077 h -5.006725 c -0.794205,0 -1.363028,-0.150257 -1.706469,-0.45077 -0.332709,-0.31124 -0.499064,-0.713709 -0.499064,-1.207406 0,-0.482966 0.17172,-0.874703 0.515161,-1.175211 0.629509,-0.456785 1.34668,-0.463302 2.205533,-0.466868 V 76.635879 c -0.626741,0.01274 -1.778837,0.03232 -2.22163,-0.45077 -0.332709,-0.31124 -0.499064,-0.713709 -0.499064,-1.207406 0,-0.493697 0.166355,-0.8908 0.499064,-1.191308 0.343441,-0.311245 0.912264,-0.466868 1.706469,-0.466868 l 5.006725,0.01607 c 0.794205,0 1.357662,0.150257 1.690371,0.45077 0.343441,0.300508 0.515162,0.697611 0.515162,1.191308 0,0.493698 -0.171721,0.896167 -0.515162,1.207406 -0.332709,0.300514 -0.896166,0.450771 -1.690371,0.450771 h -1.191317 v 4.894032 l 7.196408,-5.03892 c -0.632932,-0.386565 -0.869872,-0.75689 -0.901532,-1.48109 0,-0.504429 0.166355,-0.912264 0.499064,-1.223504 0.343441,-0.311245 0.912264,-0.466868 1.706469,-0.466868 l 3.364646,0.01607 c 0.794206,0 1.357663,0.150257 1.690372,0.45077 0.343441,0.300508 0.515161,0.697611 0.515161,1.191309 0,0.482965 -0.17172,0.880068 -0.515161,1.191308 -0.332709,0.311245 -0.858602,0.466868 -1.577679,0.466868 l -5.924355,5.183808 c 1.105452,0.633217 2.14114,1.599145 3.107065,2.897786 0.976664,1.298635 1.840632,2.908512 2.591904,4.829633 h 0.885435 c 0.783474,0 1.341565,0.155623 1.674274,0.466868 0.343441,0.300508 0.515161,0.697611 0.515161,1.191308 0,0.482965 -0.166354,0.880068 -0.499063,1.191308 -0.332709,0.300513 -0.896166,0.45077 -1.690372,0.45077 H 71.386111 C 70.69923,91.205235 69.947952,89.638286 69.132277,88.146461 68.466865,86.933686 68.044082,85.92072 67.44306,85.373363 66.85277,84.815269 65.753086,83.96452 64.926679,83.631811 Z" />
      <path
         id="karel-eye-white"
         style="fill:#ffffff"
         d="m 51.223488,22.149552 c -6.944679,-7.1e-5 -12.574497,5.629747 -12.574426,12.574426 -7.1e-5,6.944679 5.629747,12.574497 12.574426,12.574426 6.944679,7.1e-5 12.574497,-5.629747 12.574426,-12.574426 7.1e-5,-6.944679 -5.629747,-12.574497 -12.574426,-12.574426 z m 29.844689,0 c -6.944679,-7.1e-5 -12.574497,5.629747 -12.574426,12.574426 -7.1e-5,6.944679 5.629747,12.574497 12.574426,12.574426 6.944679,7.1e-5 12.574497,-5.629747 12.574426,-12.574426 7.1e-5,-6.944679 -5.629747,-12.574497 -12.574426,-12.574426 z" />
      <path
         id="karel-eye-dark"
         style="fill:#525252"
         d="m 51.223746,21.34185 c -7.381225,0 -13.383163,6.001419 -13.383163,13.382645 0,7.381227 6.001938,13.381095 13.383163,13.381095 7.381225,0 13.382645,-5.999868 13.382645,-13.381095 0,-7.381226 -6.00142,-13.382645 -13.382645,-13.382645 z m 29.844689,0 c -7.381224,0 -13.383162,6.001419 -13.383162,13.382645 0,7.381227 6.001938,13.381095 13.383162,13.381095 7.381226,0 13.382646,-5.999868 13.382646,-13.381095 0,-7.381226 -6.00142,-13.382645 -13.382646,-13.382645 z m -29.844689,1.615405 c 6.507904,0 11.76569,5.259338 11.76569,11.76724 0,6.507903 -5.257786,11.76569 -11.76569,11.76569 -6.507905,0 -11.76569,-5.257787 -11.76569,-11.76569 0,-6.507902 5.257785,-11.76724 11.76569,-11.76724 z m 29.844689,0 c 6.507905,0 11.76569,5.259338 11.76569,11.76724 0,6.507903 -5.257785,11.76569 -11.76569,11.76569 -6.507904,0 -11.76569,-5.257787 -11.76569,-11.76569 0,-6.507902 5.257786,-11.76724 11.76569,-11.76724 z m -29.844689,5.366597 c -3.534731,-1.01e-4 -6.400227,2.865396 -6.400126,6.400126 -1.01e-4,3.534731 2.865395,6.400227 6.400126,6.400126 3.53473,1.01e-4 6.400227,-2.865395 6.400126,-6.400126 1.01e-4,-3.53473 -2.865396,-6.400227 -6.400126,-6.400126 z m 29.844689,0 c -3.53473,-1.01e-4 -6.400227,2.865396 -6.400126,6.400126 -1.01e-4,3.534731 2.865396,6.400227 6.400126,6.400126 3.534731,1.01e-4 6.400227,-2.865395 6.400126,-6.400126 1.01e-4,-3.53473 -2.865395,-6.400227 -6.400126,-6.400126 z" />
      <path
         id="karel-eye-light"
         style="fill:#ffffff"
         d="m 51.223488,33.220711 a 1.5032583,1.5032583 0 0 0 -1.503268,1.503267 1.5032583,1.5032583 0 0 0 1.503268,1.503268 1.5032583,1.5032583 0 0 0 1.503267,-1.503268 1.5032583,1.5032583 0 0 0 -1.503267,-1.503267 z m 29.844689,0 a 1.5032583,1.5032583 0 0 0 -1.503267,1.503267 1.5032583,1.5032583 0 0 0 1.503267,1.503268 1.5032583,1.5032583 0 0 0 1.503268,-1.503268 1.5032583,1.5032583 0 0 0 -1.503268,-1.503267 z" />
    </svg>
    """
  end

  defp robot(:north) do
    """
    <svg viewBox="0 0 132.29166 132.29167" xmlns="http://www.w3.org/2000/svg" id="robot">
      <path
         id="karel-head"
         style="fill:#a87ace"
         d="m 40.011719,7.3203125 c -6.510771,0 -11.753907,5.2431345 -11.753907,11.7539065 v 29.927734 c 0,6.510772 5.243136,11.753906 11.753907,11.753906 17.423177,0 34.846354,0 52.269531,0 6.510772,0 11.75195,-5.243134 11.75195,-11.753906 V 19.074219 c 0,-6.510772 -5.241178,-11.7539065 -11.75195,-11.7539065 -17.423177,0 -34.846354,0 -52.269531,0 z M 22.660156,71.527343 c -4.727718,6.025773 -5.966123,15.629495 0.623047,27.566406 0.276621,-0.555726 0.639818,-1.053805 1.078125,-1.478516 0.568678,-0.509695 1.243132,-0.893584 1.990234,-1.132812 -5.635407,-10.609165 -4.238338,-17.875684 -0.591796,-22.523438 3.274277,-4.173288 8.721546,-6.197032 12.978515,-5.998046 0.318076,-1.399003 0.94551,-2.676634 1.814453,-3.753907 -7.53817,-1.05046 -14.299549,2.94312 -17.892578,7.320313 z m 69.078125,-7.320312 c 0.86887,1.077273 1.496414,2.354904 1.814453,3.753907 4.256984,-0.199027 9.704206,1.824722 12.978516,5.998046 3.64637,4.647532 5.04266,11.913148 -0.5918,22.521485 0.74726,0.239785 1.42173,0.624342 1.99024,1.134765 0.43815,0.424139 0.80133,0.921551 1.07812,1.476563 6.58823,-11.936198 5.35056,-21.538939 0.62305,-27.564453 -4.30774,-5.556815 -10.633425,-8.397957 -17.892579,-7.320313 z M 49.648438,102.9668 v 9.3789 c -0.521755,2.08582 -1.88614,2.5903 -4.181641,2.59961 -2.134295,0 -3.853516,1.6844 -3.853516,3.77735 v 7.14453 c 0,2.09296 1.719221,3.77929 3.853516,3.77929 h 14.511719 c 2.134292,0 3.851562,-1.68633 3.851562,-3.77929 v -7.14453 c 0,-2.09295 -1.71727,-3.77735 -3.851562,-3.77735 -2.788813,-0.0621 -3.698158,-1.03981 -4.160157,-2.59961 v -9.3789 z m 26.927734,0 v 9.3789 c -0.570408,2.33148 -2.10818,2.59961 -4.261719,2.59961 -2.134295,0 -3.853515,1.6844 -3.853515,3.77735 v 7.14453 c 0,2.09296 1.71922,3.77929 3.853515,3.77929 h 14.511719 c 2.134294,0 3.851562,-1.68633 3.851562,-3.77929 v -7.14453 c 0,-2.09295 -1.717268,-3.77735 -3.851562,-3.77735 -2.71012,-0.005 -3.958171,-1.03839 -4.080078,-2.59961 v -9.3789 z" />
      <path
         id="karel-body"
         style="fill:#3dbcc0"
         d="m 77.76343,2.6458333 c 0.746052,0 1.346688,0.600636 1.346688,1.3466878 v 0.5105632 c 0,0.7460518 -0.600636,1.3466878 -1.346688,1.3466878 H 67.699962 v 1.471228 H 64.591625 V 5.8497721 H 54.52764 c -0.746053,0 -1.346688,-0.600636 -1.346688,-1.3466878 V 3.9925211 c 0,-0.7460518 0.600635,-1.3466878 1.346688,-1.3466878 z m 6.745325,58.1091137 c 5.143282,0 9.283671,4.140906 9.283671,9.284188 v 23.643518 c 0,5.14328 -4.140389,9.283667 -9.283671,9.283667 H 47.782832 c -5.143282,0 -9.284188,-4.140387 -9.284188,-9.283667 V 70.039135 c 0,-5.143282 4.140906,-9.284188 9.284188,-9.284188 z m 20.461285,35.423156 c 1.13937,0.184092 2.16041,0.679731 2.96054,1.437121 0.80176,0.755639 1.35478,1.746742 1.60352,2.873736 0.15158,2.32904 0.041,4.08579 -1.34345,5.83233 -0.44875,0.47507 -1.18717,0.52568 -1.69654,0.11628 -2.47612,-2.33913 -5.04065,-4.76432 -7.290993,-6.886483 -0.437856,-0.485308 -0.429307,-1.225604 0.01964,-1.700671 2.000573,-1.507819 3.290203,-1.827397 5.747283,-1.672313 z m -71.901426,1.672754 c 0.448792,0.475208 0.457106,1.215503 0.0191,1.700671 -2.477922,2.336822 -5.042748,4.762352 -7.290534,6.886482 -0.509368,0.4094 -1.247793,0.35879 -1.69654,-0.11628 -1.413294,-1.78074 -1.819358,-4.40793 -1.479508,-5.65894 0.33985,-1.251021 0.938331,-2.291487 1.740088,-3.047125 0.799995,-0.757307 2.059844,-1.255137 3.199023,-1.439316 2.201575,-0.163716 3.495447,-0.04985 5.508371,1.674508 z" />
    </svg>
    """
  end

  defp robot(:west) do
    """
    <svg viewBox="0 0 132.29166 132.29167" xmlns="http://www.w3.org/2000/svg" id="robot">
      <path
         id="karel-body"
         style="fill:#3dbcc0"
         d="m 63.610443,2.6464844 c -0.746052,5.8e-6 -1.347655,0.5996511 -1.347656,1.3457031 v 0.5117187 c 0,0.7460521 0.601603,1.3457087 1.347656,1.3457032 h 10.0625 v 1.4707031 h 3.109375 V 5.8496094 h 10.0625 c 0.746053,-5.5e-6 1.347656,-0.5996511 1.347656,-1.3457032 V 3.9921875 c -10e-7,-0.7460422 -0.601606,-1.3456992 -1.347656,-1.3457031 z m 1.236328,58.1093746 c -5.143271,10e-7 -9.283206,4.139938 -9.283203,9.283203 v 23.644532 c 0,5.14327 4.139933,9.283206 9.283203,9.283206 h 19.128907 c 5.143272,0 9.283203,-4.139936 9.283203,-9.283206 V 70.039062 c 10e-7,-5.143274 -4.139932,-9.283205 -9.283203,-9.283203 z m -20.33789,30.841844 c -0.652357,0.04082 -1.164589,0.57498 -1.175781,1.228516 0.222116,3.463399 0.430578,6.86341 0.626997,10.009761 0.09267,0.64688 0.666126,1.11497 1.318359,1.07422 2.440617,-0.44599 4.049554,-1.86512 4.962891,-3.34554 0.587675,-0.993276 0.860951,-2.095792 0.791015,-3.195305 -0.0675,-1.099512 -0.475016,-2.157991 -1.18164,-3.070313 -1.477359,-1.990453 -3.653731,-2.770479 -5.341841,-2.701339 z" />
      <path
         id="karel-eye"
         style="fill:#525252;fill-opacity:1;stroke-width:0.286486;-inkscape-stroke:none;stop-color:#000000"
         d="m 32.58085,21.34185 v 6.921541 h -2.35276 v 13.03848 h 2.35276 v 6.803719 h 3.078045 V 21.34185 Z" />
      <path
         id="karel-head"
         style="fill:#a87ace"
         d="m 47.412801,7.3203125 c -6.51076,0 -11.753906,5.2431485 -11.753906,11.7539065 v 29.927734 c 0,6.51076 5.243146,11.753906 11.753906,11.753906 h 42.898814 c 6.510757,0 11.751955,-5.243146 11.751955,-11.753906 V 19.074219 c 0,-6.510758 -5.241198,-11.7539065 -11.751955,-11.7539065 z m 30.396861,57.7949215 -4.865234,1.314454 c 2.419387,7.083437 0.233468,16.308873 -4.720704,21.001953 -4.806772,4.553449 -9.46984,6.289582 -18.140624,7.189453 0.562878,0.835361 0.889545,1.776033 0.949218,2.748047 0.06161,0.968553 -0.144488,1.939584 -0.595703,2.835939 12.865769,-1.245544 19.807375,-6.763755 22.726563,-10.271486 2.997642,-3.602006 7.927217,-14.323944 4.646484,-24.81836 z m -7.71875,37.851566 v 9.3789 c -0.650912,2.66708 -2.79112,2.59961 -5.423828,2.59961 -2.134286,0 -3.853516,1.6844 -3.853516,3.77735 v 7.14453 c 0,2.09296 1.71923,3.77929 3.853516,3.77929 h 14.511719 c 2.134284,0 3.851562,-1.68633 3.851562,-3.77929 v -7.14453 c 0,-2.09295 -1.049308,-3.77735 -3.183594,-3.77735 -1.735385,0.12862 -3.474822,-1.11137 -3.585937,-2.59961 v -9.3789 z" />
    </svg>
    """
  end

  defp robot(:east) do
    """
    <svg viewBox="0 0 132.29166 132.29167" xmlns="http://www.w3.org/2000/svg" id="robot">
      <path
         id="karel-body"
         style="fill:#3dbcc0"
         d="m 68.681217,2.6464844 c 0.746052,5.8e-6 1.347655,0.5996511 1.347656,1.3457031 v 0.5117187 c 0,0.7460521 -0.601603,1.3457087 -1.347656,1.3457032 h -10.0625 V 7.3203125 H 55.509342 V 5.8496094 h -10.0625 C 44.700789,5.8496039 44.099186,5.2499583 44.099186,4.5039062 V 3.9921875 c 10e-7,-0.7460422 0.601606,-1.3456992 1.347656,-1.3457031 z M 67.444889,60.755859 c 5.143271,10e-7 9.283206,4.139938 9.283203,9.283203 v 23.644532 c 0,5.14327 -4.139933,9.283206 -9.283203,9.283206 H 48.315982 c -5.143272,0 -9.283203,-4.139936 -9.283203,-9.283206 V 70.039062 c -10e-7,-5.143274 4.139932,-9.283205 9.283203,-9.283203 z m 20.33789,30.841844 c 0.652357,0.04082 1.164589,0.57498 1.175781,1.228516 -0.219965,3.423283 -0.438168,6.987038 -0.626997,10.009761 -0.09267,0.64688 -0.666126,1.11497 -1.318359,1.07422 -2.281342,-0.42574 -4.049554,-1.86512 -4.962891,-3.34554 -0.587675,-0.993276 -0.860951,-2.095792 -0.791015,-3.195305 0.0675,-1.099512 0.475016,-2.157991 1.18164,-3.070313 1.179117,-1.71442 3.698879,-2.562052 5.341841,-2.701339 z" />
      <path
         id="karel-eye"
         style="fill:#525252;fill-opacity:1;stroke-width:0.286486;-inkscape-stroke:none;stop-color:#000000"
         d="m 99.71081,21.34185 v 6.921541 h 2.35276 v 13.03848 H 99.71081 V 48.10559 H 96.632765 V 21.34185 Z" />
      <path
         id="karel-head"
         style="fill:#a87ace"
         d="m 84.878859,7.3203125 c 6.51076,0 11.753906,5.2431485 11.753906,11.7539065 v 29.927734 c 0,6.51076 -5.243146,11.753906 -11.753906,11.753906 H 41.980045 c -6.510757,0 -11.751957,-5.243146 -11.751957,-11.753906 V 19.074219 c 0,-6.510758 5.2412,-11.7539065 11.751957,-11.7539065 z m -30.396861,57.7949215 4.865234,1.314454 c -2.419387,7.083437 -0.233468,16.308873 4.720704,21.001953 4.806772,4.553449 9.46984,6.289582 18.140624,7.189453 -0.562878,0.835361 -0.889545,1.776033 -0.949218,2.748047 -0.06161,0.968553 0.144488,1.939584 0.595703,2.835939 C 68.989276,98.959536 62.04767,93.441325 59.128482,89.933594 56.13084,86.331588 51.201265,75.60965 54.481998,65.115234 Z m 7.71875,37.851566 v 9.3789 c 0.890973,2.66333 2.477478,2.52614 5.423828,2.59961 2.134286,0 3.853516,1.6844 3.853516,3.77735 v 7.14453 c 0,2.09296 -1.71923,3.77929 -3.853516,3.77929 H 53.112857 c -2.134284,0 -3.851562,-1.68633 -3.851562,-3.77929 v -7.14453 c 0,-2.09295 1.049308,-3.77735 3.183594,-3.77735 1.735385,0.12862 3.572343,-0.89831 3.585937,-2.59961 v -9.3789 z" />
    </svg>
    """
  end

  def wall do
    """
    <svg viewBox="0 0 132.29201 132.29167" xmlns="http://www.w3.org/2000/svg">
      <path
         style="fill:#ffffff"
         d="M 0,0 H 132.29166 V 132.29166 H 0 Z" />
      <path
         style="fill:#acacac"
         d="M 4.7695312,0 C 3.2222028,0 1.9765625,1.2456366 1.9765625,2.7929688 V 12.625 c 0,1.547331 1.2456403,2.791016 2.7929687,2.791016 H 39.464844 c 1.547331,0 2.792968,-1.243685 2.792968,-2.791016 V 2.7929688 C 42.257812,1.2456366 41.012175,0 39.464844,0 Z M 48.798828,0 C 47.2515,0 46.005859,1.2456366 46.005859,2.7929688 V 12.625 c 0,1.547331 1.245641,2.791016 2.792969,2.791016 h 34.695313 c 1.547331,0 2.792968,-1.243685 2.792968,-2.791016 V 2.7929688 C 86.287109,1.2456366 85.041471,0 83.494141,0 Z M 92.828125,0 C 91.280794,0 90.035156,1.2456366 90.035156,2.7929688 V 12.625 c 0,1.547331 1.245638,2.791016 2.792969,2.791016 h 34.695315 c 1.54733,0 2.79297,-1.243685 2.79297,-2.791016 V 2.7929688 C 130.31641,1.2456366 129.07077,0 127.52344,0 Z M 0,19.478516 v 15.416015 h 15.621094 c 1.547326,0 2.792968,-1.245641 2.792968,-2.792969 v -9.830078 c 0,-1.547317 -1.245642,-2.792968 -2.792968,-2.792968 z m 25.53125,0 c -1.547331,0 -2.792969,1.245646 -2.792969,2.792968 v 9.830078 c 0,1.547328 1.245638,2.794922 2.792969,2.794922 h 35.660156 c 1.547333,0 2.792969,-1.247594 2.792969,-2.794922 v -9.830078 c 0,-1.547322 -1.245636,-2.792968 -2.792969,-2.792968 z m 45.570312,0 c -1.547331,0 -2.792968,1.245646 -2.792968,2.792968 v 9.830078 c 0,1.547328 1.245637,2.794922 2.792968,2.794922 h 35.658208 c 1.54733,0 2.79492,-1.247594 2.79492,-2.794922 v -9.830078 c 0,-1.547322 -1.24759,-2.792968 -2.79492,-2.792968 z m 45.570318,0 c -1.54733,0 -2.79297,1.245651 -2.79297,2.792968 v 9.830078 c 0,1.547328 1.24564,2.792969 2.79297,2.792969 5.20703,0 10.41406,0 15.62109,0 0,-5.138672 0,-10.277343 0,-15.416015 -5.20703,0 -10.41406,0 -15.62109,0 z M 4.7695312,38.958984 c -1.5473284,0 -2.7929687,1.245637 -2.7929687,2.792969 v 9.832031 c 0,1.547332 1.2456403,2.791016 2.7929687,2.791016 H 39.464844 c 1.547331,0 2.792968,-1.243684 2.792968,-2.791016 v -9.832031 c 0,-1.547332 -1.245637,-2.792969 -2.792968,-2.792969 z m 44.0292968,0 c -1.547328,0 -2.792969,1.245637 -2.792969,2.792969 v 9.832031 c 0,1.547332 1.245641,2.791016 2.792969,2.791016 h 34.695313 c 1.547331,0 2.792968,-1.243684 2.792968,-2.791016 v -9.832031 c 0,-1.547332 -1.245638,-2.792969 -2.792968,-2.792969 z m 44.029297,0 c -1.547331,0 -2.792969,1.245637 -2.792969,2.792969 v 9.832031 c 0,1.547332 1.245638,2.791016 2.792969,2.791016 h 34.695315 c 1.54733,0 2.79297,-1.243684 2.79297,-2.791016 v -9.832031 c 0,-1.547332 -1.24564,-2.792969 -2.79297,-2.792969 z M 0,58.4375 c 0,5.138672 0,10.277344 0,15.416016 5.2070313,0 10.414063,0 15.621094,0 1.547326,0 2.792968,-1.245644 2.792968,-2.792969 v -9.830078 c 0,-1.547319 -1.245642,-2.792969 -2.792968,-2.792969 -5.207031,0 -10.4140627,0 -15.621094,0 z m 25.53125,0 c -1.547331,0 -2.792969,1.245645 -2.792969,2.792969 v 9.830078 c 0,1.547325 1.245638,2.794922 2.792969,2.794922 h 35.660156 c 1.547333,0 2.792969,-1.247597 2.792969,-2.794922 v -9.830078 c 0,-1.547324 -1.245636,-2.792969 -2.792969,-2.792969 z m 45.570312,0 c -1.547331,0 -2.792968,1.245645 -2.792968,2.792969 v 9.830078 c 0,1.547325 1.245637,2.794922 2.792968,2.794922 h 35.658208 c 1.54733,0 2.79492,-1.247597 2.79492,-2.794922 v -9.830078 c 0,-1.547324 -1.24759,-2.792969 -2.79492,-2.792969 z m 45.570318,0 c -1.54733,0 -2.79297,1.24565 -2.79297,2.792969 v 9.830078 c 0,1.547325 1.24564,2.792969 2.79297,2.792969 5.20703,0 10.41406,0 15.62109,0 0,-5.138672 0,-10.277344 0,-15.416016 -5.20703,0 -10.41406,0 -15.62109,0 z M 4.7695312,77.917969 c -1.5473284,0 -2.7929687,1.245636 -2.7929687,2.792969 v 9.832031 c 0,1.547331 1.2456403,2.791015 2.7929687,2.791015 H 39.464844 c 1.547331,0 2.792968,-1.243684 2.792968,-2.791015 v -9.832031 c 0,-1.547333 -1.245637,-2.792969 -2.792968,-2.792969 z m 44.0292968,0 c -1.547328,0 -2.792969,1.245636 -2.792969,2.792969 v 9.832031 c 0,1.547331 1.245641,2.791015 2.792969,2.791015 h 34.695313 c 1.547331,0 2.792968,-1.243684 2.792968,-2.791015 v -9.832031 c 0,-1.547333 -1.245638,-2.792969 -2.792968,-2.792969 z m 44.029297,0 c -1.547331,0 -2.792969,1.245636 -2.792969,2.792969 v 9.832031 c 0,1.547331 1.245638,2.791015 2.792969,2.791015 h 34.695315 c 1.54733,0 2.79297,-1.243684 2.79297,-2.791015 v -9.832031 c 0,-1.547333 -1.24564,-2.792969 -2.79297,-2.792969 z M 0,97.396484 c 0,5.138676 0,10.277346 0,15.416016 5.2070313,0 10.414063,0 15.621094,0 1.547326,0 2.792968,-1.24564 2.792968,-2.79297 v -9.83008 c 0,-1.547309 -1.245642,-2.792966 -2.792968,-2.792966 -5.207031,0 -10.4140627,0 -15.621094,0 z m 25.53125,0 c -1.547331,0 -2.792969,1.245652 -2.792969,2.792966 v 9.83008 c 0,1.54733 1.245638,2.79297 2.792969,2.79297 h 35.660156 c 1.547333,0 2.792969,-1.24564 2.792969,-2.79297 v -9.83008 c 0,-1.547314 -1.245636,-2.792966 -2.792969,-2.792966 z m 45.570312,0 c -1.547331,0 -2.792968,1.245652 -2.792968,2.792966 v 9.83008 c 0,1.54733 1.245637,2.79297 2.792968,2.79297 h 35.658208 c 1.54733,0 2.79492,-1.24564 2.79492,-2.79297 v -9.83008 c 0,-1.547314 -1.24759,-2.792966 -2.79492,-2.792966 z m 45.570318,0 c -1.54733,0 -2.79297,1.245657 -2.79297,2.792966 v 9.83008 c 0,1.54733 1.24564,2.79297 2.79297,2.79297 5.20703,0 10.41406,0 15.62109,0 0,-5.13867 0,-10.27734 0,-15.416016 -5.20703,0 -10.41406,0 -15.62109,0 z M 4.7695312,116.875 c -1.5473284,0 -2.7929687,1.24564 -2.7929687,2.79297 V 129.5 c 0,1.54733 1.2456403,2.79102 2.7929687,2.79102 H 39.464844 c 1.547331,0 2.792968,-1.24369 2.792968,-2.79102 v -9.83203 c 0,-1.54733 -1.245637,-2.79297 -2.792968,-2.79297 z m 44.0292968,0 c -1.547328,0 -2.792969,1.24564 -2.792969,2.79297 V 129.5 c 0,1.54733 1.245641,2.79102 2.792969,2.79102 h 34.695313 c 1.547331,0 2.792968,-1.24369 2.792968,-2.79102 v -9.83203 c 0,-1.54733 -1.245638,-2.79297 -2.792968,-2.79297 z m 44.029297,0 c -1.547331,0 -2.792969,1.24564 -2.792969,2.79297 V 129.5 c 0,1.54733 1.245638,2.79102 2.792969,2.79102 h 34.695315 c 1.54733,0 2.79297,-1.24369 2.79297,-2.79102 v -9.83203 c 0,-1.54733 -1.24564,-2.79297 -2.79297,-2.79297 z" />
    </svg>
    """
  end
end

Kino.nothing()

Code

defmodule Karel.Board do
  defstruct [:cols, :rows, :walls, colors: %{}]

  def new(opts \\ []) do
    size = Keyword.get(opts, :size, 10)
    cols = Keyword.get(opts, :cols, size)
    rows = Keyword.get(opts, :rows, size)
    walls = MapSet.new(Keyword.get(opts, :walls, []))
    %__MODULE__{cols: cols, rows: rows, walls: walls}
  end

  def wall?(board, x, y) do
    not (x in 1..board.cols and y in 1..board.rows) or MapSet.member?(board.walls, {x, y})
  end

  def put_color(board, x, y, color) do
    put_in(board.colors[{x, y}], color)
  end

  def clear_color(board, x, y) do
    %{board | colors: Map.delete(board.colors, {x, y})}
  end

  def color(board, x, y) do
    Map.get(board.colors, {x, y})
  end
end

defmodule Karel.Robot do
  defstruct x: 1, y: 1, dir: :south

  def step(robot) do
    case robot.dir do
      :south -> %{robot | y: robot.y + 1}
      :north -> %{robot | y: robot.y - 1}
      :east -> %{robot | x: robot.x + 1}
      :west -> %{robot | x: robot.x - 1}
    end
  end

  def turn(robot, dir) do
    case {robot.dir, dir} do
      {:south, :left} -> %{robot | dir: :east}
      {:south, :right} -> %{robot | dir: :west}
      {:east, :left} -> %{robot | dir: :north}
      {:east, :right} -> %{robot | dir: :south}
      {:north, :left} -> %{robot | dir: :west}
      {:north, :right} -> %{robot | dir: :east}
      {:west, :left} -> %{robot | dir: :south}
      {:west, :right} -> %{robot | dir: :north}
    end
  end
end

defmodule Karel.Game do
  alias Karel.Board
  alias Karel.Robot

  defstruct [:board, :robot]

  def new(opts \\ []) do
    robot = %Robot{}
    walls = Keyword.get(opts, :walls, []) |> Enum.reject(&(&1 == {robot.x, robot.y}))
    %__MODULE__{board: Board.new(size: 10, walls: walls), robot: robot}
  end

  def step(game) do
    robot = Robot.step(game.robot)

    if not Board.wall?(game.board, robot.x, robot.y) do
      {:ok, %{game | robot: robot}}
    else
      {:error, :wall}
    end
  end

  def turn(game, dir) do
    robot = Robot.turn(game.robot, dir)
    %{game | robot: robot}
  end

  def put_color(game, color) do
    robot = game.robot
    %{game | board: Board.put_color(game.board, robot.x, robot.y, color)}
  end

  def clear_color(game) do
    robot = game.robot
    %{game | board: Board.clear_color(game.board, robot.x, robot.y)}
  end
end

Kino.nothing()
defmodule Karel.Kino do
  use Kino.JS
  use Kino.JS.Live
  alias Karel.Board
  alias Karel.Game
  alias Karel.Images

  def new(game) do
    Kino.JS.Live.new(__MODULE__, game)
  end

  def reset(kino, game) do
    Kino.JS.Live.call(kino, {:reset, game})
  end

  def step(kino) do
    Kino.JS.Live.call(kino, :step)
  end

  def turn(kino, dir) when dir in [:left, :right] do
    Kino.JS.Live.call(kino, {:turn, dir})
  end

  def put_color(kino, color) do
    Kino.JS.Live.call(kino, {:put_color, color})
  end

  def clear_color(kino) do
    Kino.JS.Live.call(kino, :clear_color)
  end

  @impl true
  def init(game, ctx) do
    {:ok, assign(ctx, game: game)}
  end

  @impl true
  def handle_connect(ctx) do
    {:ok, init_data(ctx.assigns.game), ctx}
  end

  @impl true
  def handle_call(:step, _, ctx) do
    case Game.step(ctx.assigns.game) do
      {:ok, game} ->
        robot = game.robot
        broadcast_event(ctx, "step", %{x: robot.x, y: robot.y, dir: robot.dir})
        {:reply, :ok, assign(ctx, game: game)}

      {:error, _} = error ->
        {:reply, error, ctx}
    end
  end

  def handle_call({:turn, dir}, _, ctx) do
    game = Game.turn(ctx.assigns.game, dir)
    robot = game.robot
    broadcast_event(ctx, "turn", %{x: robot.x, y: robot.y, dir: robot.dir})
    {:reply, :ok, assign(ctx, game: game)}
  end

  def handle_call({:put_color, color}, _, ctx) do
    color = sanitize_color(color)
    game = Game.put_color(ctx.assigns.game, color)
    robot = game.robot
    broadcast_event(ctx, "put-color", %{x: robot.x, y: robot.y, color: color})
    {:reply, :ok, assign(ctx, game: game)}
  end

  def handle_call(:clear_color, _, ctx) do
    game = Game.clear_color(ctx.assigns.game)
    robot = game.robot
    broadcast_event(ctx, "clear-color", %{x: robot.x, y: robot.y})
    {:reply, :ok, assign(ctx, game: game)}
  end

  def handle_call({:reset, game}, _, ctx) do
    data = init_data(game) |> Map.delete(:images)
    broadcast_event(ctx, "reset", data)
    {:reply, :ok, assign(ctx, game: game)}
  end

  defp init_data(game) do
    %{
      cols: game.board.cols,
      html: html(game.board),
      images: %{robot: Images.robot()},
      x: game.robot.x,
      y: game.robot.y,
      dir: game.robot.dir
    }
  end

  defp html(board) do
    ~s|<div class="board">#{html_fields(board)}</div>|
  end

  defp html_fields(board) do
    for y <- 1..board.rows, x <- 1..board.cols do
      wall = if Board.wall?(board, x, y), do: Images.wall()
      color = Board.color(board, x, y)
      color = if color, do: ~s|style="--bg-color: #{color}"|
      ~s|<div id="board-field-#{x}-#{y}" #{color}>#{wall}</div>|
    end
  end

  defp sanitize_color(color) do
    color
    |> String.to_charlist()
    |> Enum.filter(&(&1 in ?a..?z or &1 in ?A..?Z))
    |> List.to_string()
  end

  asset "main.js" do
    """
    export function init(ctx, data) {
      document.querySelector(':root').style.setProperty('--board-cols', data.cols);
      ctx.importCSS("main.css")
      ctx.root.innerHTML = data.html
      fieldElement(data.x, data.y).innerHTML = data.images.robot[data.dir]

      ctx.handleEvent("reset", (r) => {
        ctx.root.innerHTML = r.html
        fieldElement(r.x, r.y).innerHTML = data.images.robot[data.dir]
      });

      ctx.handleEvent("step", (step) => {
        var robot = document.getElementById("robot")
        robot.remove()
        robot.setAttribute("data-move-dir", step.dir)
        fieldElement(step.x, step.y).appendChild(robot)
      });

      ctx.handleEvent("turn", (turn) => {
        var robot = document.getElementById("robot")
        robot.remove()
        fieldElement(turn.x, turn.y).innerHTML = data.images.robot[turn.dir]
      });

      ctx.handleEvent("put-color", (pc) => {
        fieldElement(pc.x, pc.y).style.setProperty('--bg-color', pc.color)
      });

      ctx.handleEvent("clear-color", (cc) => {
        fieldElement(cc.x, cc.y).style.removeProperty('--bg-color')
      });
    }

    function fieldElement(x, y) {
      return document.getElementById(fieldId(x, y))
    }

    function fieldId(x, y) {
      return `board-field-${x}-${y}`
    }
    """
  end

  asset "main.css" do
    """
    :root {
      --border-color: White;
      --border-size: 2px;
      --bg-color: LightGrey;
      --step-duration: 0.5s;
    }
    .board {
      background: var(--border-color);
      border: var(--border-size) solid var(--border-color);
      display: grid;
      grid-gap: var(--border-size);
      grid-template-columns: repeat(var(--board-cols), auto);
    }
    .board div {
      background: var(--bg-color);
      aspect-ratio: 1/1;
      position: relative;
    }
    .board div svg {
      position: absolute;
      width: 100%;
      z-index: 1;
    }
    svg[data-move-dir="south"] {
      animation-duration: var(--step-duration);
      animation-name: slideSouth;
    }
    @keyframes slideSouth {
      from { top: calc(-100% - var(--border-size)); }
      to { top: 0px; }
    }
    svg[data-move-dir="east"] {
      animation-duration: var(--step-duration);
      animation-name: slideEast;
    }
    @keyframes slideEast {
      from { left: calc(-100% - var(--border-size)); }
      to { left: 0px; }
    }
    svg[data-move-dir="north"] {
      animation-duration: var(--step-duration);
      animation-name: slideNorth;
    }
    @keyframes slideNorth {
      from { top: calc(100% + var(--border-size)); }
      to { top: 0px; }
    }
    svg[data-move-dir="west"] {
      animation-duration: var(--step-duration);
      animation-name: slideWest;
    }
    @keyframes slideWest {
      from { left: calc(100% + var(--border-size)); }
      to { left: 0px; }
    }
    """
  end
end

Kino.nothing()
defmodule Karel.AI do
  alias LangChain.Chains.LLMChain
  alias LangChain.ChatModels.ChatOpenAI
  alias LangChain.Message

  def openai_key() do
    case System.fetch_env("LB_OPENAI_KEY") do
      {:ok, key} ->
        Application.put_env(:langchain, :openai_key, key)
        :ok

      :error ->
        {:error, "OPENAI_KEY is not set in Secrets"}
    end
  end

  def init() do
    width = 10
    height = 10

    %{llm: ChatOpenAI.new!(%{model: "gpt-3.5-turbo"})}
    |> LLMChain.new!()
    |> LLMChain.add_messages([
      Message.new_system!("""
      You are controlling movement of a robot on a board of a size #{width} x #{height}.
      You will translate instructions for a robot from natural language to JSON.

      You can use only these 4 commands:
      "step", "turn-left", "turn-right", {"put-color": "ColorName"}

      Unless the instruction is to "draw", you are allowed to use only
      "step", "turn-left", "turn-right" commands!

      If the instruction is to "draw",
      you will use the {"put-color": "ColorName"} command as well.
      You will use it in a following way:
      you will prepend each "step" in the set of instructions with {"put-color": "ColorName"},
      where as a ColorName you will use an existing HTML Color Name most similar to the specified color.

      Here are a few examples:

      Input: Go one step forward.
      Output: ["step"]
      ---
      Input: Move two steps forward.
      Output: ["step", "step"]
      ---
      Input: Turn right.
      Output: ["turn-right"]
      ---
      Input: Go five steps forward, then turn left, then move three steps forward.
      Output: ["step", "step", "step", "step", "step", "turn-left", "step", "step", "step"]
      ---
      Input: Draw medium purple.
      Output: [{"put-color": "MediumPurple"}]
      ---
      Input: Go around a rectangle 2x3.
      Output: ["step", "step", "turn-right", "step", "step", "step", "turn-right", "step", "step", "turn-right", "step", "step", "step"]
      ---
      Input: Draw turquoise line 3 units long.
      Output: [{"put-color": "Turquoise"}, "step", {"put-color": "Turquoise"}, "step", {"put-color": "Turquoise"}, "step"]
      ---
      Input: Draw a tomato square 3x3.
      Output: [{"put-color": "Tomato"}, "step", {"put-color": "Tomato"}, "step", {"put-color": "Tomato"}, "turn-right", "step", {"put-color": "Tomato"}, "step", {"put-color": "Tomato"}, "turn-right", "step", {"put-color": "Tomato"}, "step", {"put-color": "Tomato"}, "turn-right", "step", {"put-color": "Tomato"}, "step"]
      ---
      Input: Draw a violet square with a side size of 5.
      Output: [{"put-color": "violet"}, "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "turn-right", "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "turn-right", "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "turn-right", "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "step", {"put-color": "violet"}, "step"]

      Mind the correct usage of brackets in syntax!

      You reply will include only the content of the output, for example like this:
      ["turn-right", "step", "step", "turn-left", step", "turn-right", "step"]
      """)
    ])
    |> LLMChain.run()
  end

  def translate(updated_chain, text) do
    with {:ok, _updated_chain, response} <-
           updated_chain
           |> LLMChain.add_message(Message.new_user!(text))
           |> LLMChain.run() do
      {:ok, response.content}
    end
  end
end

Kino.nothing()
defmodule Karel do
  def run_script(karel, text) do
    with {:ok, script} <- parse_script(text) do
      Enum.reduce_while(script, :ok, fn instr, acc ->
        case cmd(karel, instr) do
          :ok -> {:cont, acc}
          error -> {:halt, error}
        end
      end)
    end
  end

  def run_ai(karel, chain, text) do
    with {:ok, script} <- Karel.AI.translate(chain, text) do
      Karel.run_script(karel, script)
    end
  end

  defp parse_script(text) do
    Jason.decode(text)
  end

  defp cmd(karel, "step") do
    with :ok <- Karel.Kino.step(karel) do
      Process.sleep(500)
      :ok
    end
  end

  defp cmd(karel, "turn-right") do
    with :ok <- Karel.Kino.turn(karel, :right) do
      Process.sleep(100)
      :ok
    end
  end

  defp cmd(karel, "turn-left") do
    with :ok <- Karel.Kino.turn(karel, :left) do
      Process.sleep(100)
      :ok
    end
  end

  defp cmd(karel, %{"put-color" => color}) do
    Karel.Kino.put_color(karel, color)
  end

  defp cmd(karel, "clear-color") do
    Karel.Kino.clear_color(karel)
  end

  defp cmd(_karel, invalid) do
    {:error, {:invalid_cmd, invalid}}
  end
end

Kino.nothing()
defmodule Karel.Help do
  def text do
    """
    Karel understands 5 basic instructions: `turn-left`, `turn-right`, `step` (move one step forward), `put-color` (paint the cell he is standing on with a specified color) and `clear-color` (remove the color).

    You can control Karel in 3 different ways: **with a script**, **with AI** (natural language) or **manually** (buttons).

    ### Script examples

    ```
    [
      "step", "step", "turn-right", "step", "step",
      "turn-right", "step", "step",
      "turn-right", "step", "step"
    ]
    ```

    You can use this syntax to paint: `{"put-color": "HTMLColorName"}`

    ```
    [
      {"put-color": "purple"}, "step",
      {"put-color": "purple"}, "step",
      {"put-color": "purple"}, "turn-left", "step",
      {"put-color": "purple"}, "step",
      {"put-color": "purple"}, "turn-left", "step",
      {"put-color": "purple"}, "step",
      {"put-color": "purple"}
    ]
    ```

    ### AI examples

    You can try these: `Go 3 steps forward, then turn left, then move two steps.`

    `Draw red, move 3 steps, then draw pink, turn left, go 2 steps and draw orange.`

    `Draw a yellow line 4 units long.`

    _Note 1: For this feature to work you need to have tokens (credit)._

    _Note 2: Sometimes it takes a while to translate the AI instructions and for Karel to start walking._
    """
  end
end

Kino.nothing()
defmodule Karel.UI do
  alias Kino.Layout

  def new do
    default_walls = "[[10, 8], [6, 10], [7, 10], [2, 6]]"
    {:ok, walls} = convert_walls(default_walls)

    karel = Karel.Kino.new(Karel.Game.new(walls: walls))

    step_button = Kino.Control.button("step")
    turn_left_button = Kino.Control.button("turn left")
    turn_right_button = Kino.Control.button("turn right")
    reset_button = Kino.Control.button("reset board")

    walls_text = Kino.Input.textarea("walls", default: default_walls, monospace: true)

    script_text = Kino.Input.textarea("", default: default_script(), monospace: true)
    script_form = Kino.Control.form([script: script_text], submit: "run")

    ai_text = Kino.Input.textarea("", default: "go 2 steps", monospace: true)
    ai_form = Kino.Control.form([script: ai_text], submit: "run")

    output = Kino.Frame.new()

    events = [
      step: step_button,
      turn_left: turn_left_button,
      turn_right: turn_right_button,
      walls_change: walls_text,
      reset: reset_button,
      script_form: script_form,
      ai_form: ai_form
    ]

    stream = Kino.Control.tagged_stream(events)

    Kino.listen(stream, %{out: [], output: output, walls: walls}, fn
      {:step, %{type: :click}}, state ->
        state = report(state, Karel.Kino.step(karel))
        {:cont, state}

      {:turn_left, %{type: :click}}, state ->
        state = report(state, Karel.Kino.turn(karel, :left))
        {:cont, state}

      {:turn_right, %{type: :click}}, state ->
        state = report(state, Karel.Kino.turn(karel, :right))
        {:cont, state}

      {:walls_change, %{type: :change, value: text}}, state ->
        state =
          case convert_walls(text) do
            {:ok, walls} -> %{state | walls: walls}
            error -> report(state, error)
          end

        {:cont, state}

      {:reset, %{type: :click}}, state ->
        state =
          case Karel.Kino.reset(karel, Karel.Game.new(walls: state.walls)) do
            :ok ->
              Kino.Frame.clear(state.output)
              %{state | out: []}

            error ->
              report(state, error)
          end

        {:cont, state}

      {:script_form, %{data: %{script: script}, type: :submit}}, state ->
        state = report(state, Karel.run_script(karel, script))
        {:cont, state}

      {:ai_form, %{data: %{script: script}, type: :submit}}, state ->
        state = info(state, "calling AI started")

        state =
          case init_ai(state) do
            {:ok, state} ->
              report(state, Karel.run_ai(karel, state.ai, script))

            error ->
              report(state, error)
          end

        state = info(state, "calling AI finished")
        {:cont, state}
    end)

    Layout.grid([
      Layout.grid(
        [
          karel,
          Layout.tabs(
            script: Layout.grid([script_form]),
            ai: Layout.grid([ai_form]),
            manual:
              Layout.grid([
                Layout.grid([turn_left_button, step_button, turn_right_button], columns: 3),
                walls_text,
                reset_button
              ]),
            help: Kino.Markdown.new(Karel.Help.text())
          )
        ],
        columns: 2
      ),
      output
    ])
  end

  defp init_ai(state) do
    case state[:ai] do
      nil ->
        with :ok <- Karel.AI.openai_key(),
             {:ok, chain, _response} <- Karel.AI.init() do
          {:ok, Map.put(state, :ai, chain)}
        end

      _chain ->
        {:ok, state}
    end
  end

  defp convert_walls(text) do
    case Jason.decode(text) do
      {:ok, walls} when is_list(walls) ->
        walls =
          walls
          |> Enum.filter(fn
            [x, y] when is_integer(x) and is_integer(y) -> true
            _ -> false
          end)
          |> Enum.map(fn [x, y] -> {x, y} end)

        {:ok, walls}

      {:ok, other} ->
        {:error, "invalid walls: #{inspect(other)}"}

      error ->
        error
    end
  end

  defp report(state, :ok) do
    state
  end

  defp report(state, {:error, :wall}) do
    info(state, "Karel hit the Wall!")
  end

  defp report(state, {:error, {:invalid_cmd, cmd}}) do
    info(state, "Karel is confused with command #{inspect(cmd)}")
  end

  defp report(state, {:error, _} = result) do
    info(state, result)
  end

  defp info(state, msg) do
    out = Enum.take([{"#{DateTime.utc_now(:millisecond)}", msg} | state.out], 10)
    Kino.Frame.clear(state.output)
    Enum.each(out, &Kino.Frame.append(state.output, &1))
    %{state | out: out}
  end

  defp default_script do
    """
    [
      "step", "step", "turn-left", "step", "step",
      "turn-right",
      {"put-color": "DarkOrange"}, "step",
      {"put-color": "DarkOrange"}, "step",
      {"put-color": "DarkOrange"}, "step",
      {"put-color": "DarkOrange"}, "step",
      {"put-color": "DarkOrange"},
      "turn-left", "step", "step", "turn-left",
      {"put-color": "DarkOrange"}, "step",
      {"put-color": "DarkOrange"}, "step",
      {"put-color": "DarkOrange"}, "step",
      {"put-color": "DarkOrange"}, "step",
      {"put-color": "DarkOrange"},
      "turn-left", "turn-left", "step", "step",
      "turn-right", "step",
      {"put-color": "DarkOrange"},
      "turn-right", "turn-right",
      "step", "step", "step",
      "turn-right",
      {"put-color": "RoyalBlue"}, "step",
      {"put-color": "RoyalBlue"}, "step",
      {"put-color": "RoyalBlue"},
      "turn-right", "turn-right",
      "step", "step", "step",
      {"put-color": "RoyalBlue"}, "step",
      {"put-color": "RoyalBlue"}, "step",
      "turn-right", "turn-right"
    ]
    """
  end
end

Kino.nothing()
Karel.UI.new()