cHl0aG9uaW1wb3J0IGdvb2dsZS5jb2xhYi5vdXRwdXQKaW1wb3J0IHJhbmRvbQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAxLiDpmpzlrrPnianjgojjgZHjgqLjgq/jgrfjg6fjg7PjgrLjg7zjg6DvvIjjgq3jg6Xjg7zjg5bjg6njg7PvvIkKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KZ29vZ2xlLmNvbGFiLm91dHB1dC5odG1sKCcnJwombHQ7ZGl2IHN0eWxlPSZxdW90O3RleHQtYWxpZ246IGNlbnRlcjsgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7IG1hcmdpbi1ib3R0b206IDQwcHg7JnF1b3Q7Jmd0OwogICAgJmx0O2gzJmd0O+KRoCDjgq3jg6Xjg7zjg5bjg6njg7PvvIjjgq/jg6rjg4Pjgq/jgafjgrjjg6Pjg7Pjg5fvvIHvvIkmbHQ7L2gzJmd0OwogICAgJmx0O2NhbnZhcyBpZD0mcXVvdDtnYW1lQ2FudmFzJnF1b3Q7IHdpZHRoPSZxdW90OzQ4MCZxdW90OyBoZWlnaHQ9JnF1b3Q7MjAwJnF1b3Q7IHN0eWxlPSZxdW90O2JvcmRlcjoycHggc29saWQgIzMzMzsgYmFja2dyb3VuZDojZjdmN2Y3OyZxdW90OyZndDsmbHQ7L2NhbnZhcyZndDsKICAgICZsdDtkaXYgaWQ9JnF1b3Q7c2NvcmVCb2FyZCZxdW90OyBzdHlsZT0mcXVvdDtmb250LXNpemU6IDIwcHg7IG1hcmdpbi10b3A6IDEwcHg7JnF1b3Q7Jmd0O1NDT1JFOiAwJmx0Oy9kaXYmZ3Q7CiAgICAmbHQ7YnV0dG9uIGlkPSZxdW90O3JldHJ5QnRuJnF1b3Q7IHN0eWxlPSZxdW90O2Rpc3BsYXk6bm9uZTsgbWFyZ2luOiAxMHB4IGF1dG87IHBhZGRpbmc6IDVweCAxNXB4OyBmb250LXNpemU6MTZweDsmcXVvdDsmZ3Q744KC44GG5LiA5bqm44OX44Os44KkJmx0Oy9idXR0b24mZ3Q7CiZsdDsvZGl2Jmd0OwoKJmx0O3NjcmlwdCZndDsKKGZ1bmN0aW9uKCkgewogICAgY29uc3QgY2FudmFzID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJnF1b3Q7Z2FtZUNhbnZhcyZxdW90Oyk7CiAgICBjb25zdCBjdHggPSBjYW52YXMuZ2V0Q29udGV4dCgmcXVvdDsyZCZxdW90Oyk7CiAgICBjb25zdCBzY29yZUJvYXJkID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJnF1b3Q7c2NvcmVCb2FyZCZxdW90Oyk7CiAgICBjb25zdCByZXRyeUJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCZxdW90O3JldHJ5QnRuJnF1b3Q7KTsKCiAgICBsZXQgcGxheWVyLCBvYnN0YWNsZXMsIHNjb3JlLCBnYW1lQWN0aXZlLCBzcGVlZDsKCiAgICBmdW5jdGlvbiBpbml0KCkgewogICAgICAgIHBsYXllciA9IHsgeDogNTAsIHk6IDE1MCwgd3k6IDAsIHNpemU6IDIwLCBpc0p1bXBpbmc6IGZhbHNlIH07CiAgICAgICAgb2JzdGFjbGVzID0gW107CiAgICAgICAgc2NvcmUgPSAwOwogICAgICAgIHNwZWVkID0gNDsKICAgICAgICBnYW1lQWN0aXZlID0gdHJ1ZTsKICAgICAgICByZXRyeUJ0bi5zdHlsZS5kaXNwbGF5ID0gJnF1b3Q7bm9uZSZxdW90OzsKICAgICAgICBzY29yZUJvYXJkLmlubmVyVGV4dCA9ICZxdW90O1NDT1JFOiAwJnF1b3Q7OwogICAgICAgIGxvb3AoKTsKICAgIH0KCiAgICBmdW5jdGlvbiBzcGF3bk9ic3RhY2xlKCkgewogICAgICAgIGlmICghZ2FtZUFjdGl2ZSkgcmV0dXJuOwogICAgICAgIG9ic3RhY2xlcy5wdXNoKHsgeDogY2FudmFzLndpZHRoLCB5OiAxNTAsIHdpZHRoOiAxNSwgaGVpZ2h0OiAyMCArIE1hdGgucmFuZG9tKCkgKiAyMCB9KTsKICAgICAgICBzZXRUaW1lb3V0KHNwYXduT2JzdGFjbGUsIDEyMDAgKyBNYXRoLnJhbmRvbSgpICogMTAwMCk7CiAgICB9CgogICAgZnVuY3Rpb24ganVtcCgpIHsKICAgICAgICBpZiAoIXBsYXllci5pc0p1bXBpbmcgJmFtcDsmYW1wOyBnYW1lQWN0aXZlKSB7CiAgICAgICAgICAgIHBsYXllci53eSA9IC04OwogICAgICAgICAgICBwbGF5ZXIuaXNKdW1waW5nID0gdHJ1ZTsKICAgICAgICB9CiAgICB9CgogICAgY2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoJnF1b3Q7Y2xpY2smcXVvdDssIGp1bXApOwogICAgcmV0cnlCdG4uYWRkRXZlbnRMaXN0ZW5lcigmcXVvdDtjbGljayZxdW90OywgaW5pdCk7CgogICAgZnVuY3Rpb24gbG9vcCgpIHsKICAgICAgICBpZiAoIWdhbWVBY3RpdmUpIHJldHVybjsKICAgICAgICBjdHguY2xlYXJSZWN0KDAsIDAsIGNhbnZhcy53aWR0aCwgY2FudmFzLmhlaWdodCk7CiAgICAgICAgY3R4LmZpbGxTdHlsZSA9ICZxdW90OyM2NjYmcXVvdDs7CiAgICAgICAgY3R4LmZpbGxSZWN0KDAsIDE3MCwgY2FudmFzLndpZHRoLCAzMCk7CgogICAgICAgIHBsYXllci55ICs9IHBsYXllci53eTsKICAgICAgICBwbGF5ZXIud3kgKz0gMC40OwogICAgICAgIGlmIChwbGF5ZXIueSAmZ3Q7PSAxNTApIHsKICAgICAgICAgICAgcGxheWVyLnkgPSAxNTA7CiAgICAgICAgICAgIHBsYXllci5pc0p1bXBpbmcgPSBmYWxzZTsKICAgICAgICB9CgogICAgICAgIGN0eC5maWxsU3R5bGUgPSAmcXVvdDsjMzQ5OGRiJnF1b3Q7OwogICAgICAgIGN0eC5maWxsUmVjdChwbGF5ZXIueCwgcGxheWVyLnksIHBsYXllci5zaXplLCBwbGF5ZXIuc2l6ZSk7CgogICAgICAgIGZvciAobGV0IGkgPSBvYnN0YWNsZXMubGVuZ3RoIC0gMTsgaSAmZ3Q7PSAwOyBpLS0pIHsKICAgICAgICAgICAgbGV0IG9icyA9IG9ic3RhY2xlc1tpXTsKICAgICAgICAgICAgb2JzLnggLT0gc3BlZWQ7CgogICAgICAgICAgICBjdHguZmlsbFN0eWxlID0gJnF1b3Q7I2U3NGMzYyZxdW90OzsKICAgICAgICAgICAgY3R4LmZpbGxSZWN0KG9icy54LCBvYnMueSwgb2JzLndpZHRoLCBvYnMuaGVpZ2h0KTsKCiAgICAgICAgICAgIGlmIChwbGF5ZXIueCAmbHQ7IG9icy54ICsgb2JzLndpZHRoICZhbXA7JmFtcDsKICAgICAgICAgICAgICAgIHBsYXllci54ICsgcGxheWVyLnNpemUgJmd0OyBvYnMueCAmYW1wOyZhbXA7CiAgICAgICAgICAgICAgICBwbGF5ZXIueSAmbHQ7IG9icy55ICsgb2JzLmhlaWdodCAmYW1wOyZhbXA7CiAgICAgICAgICAgICAgICBwbGF5ZXIueSArIHBsYXllci5zaXplICZndDsgb2JzLnkpIHsKICAgICAgICAgICAgICAgIGdhbWVBY3RpdmUgPSBmYWxzZTsKICAgICAgICAgICAgICAgIGN0eC5maWxsU3R5bGUgPSAmcXVvdDtyZ2JhKDAsMCwwLDAuNSkmcXVvdDs7CiAgICAgICAgICAgICAgICBjdHguZmlsbFJlY3QoMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0KTsKICAgICAgICAgICAgICAgIGN0eC5maWxsU3R5bGUgPSAmcXVvdDsjZmZmJnF1b3Q7OwogICAgICAgICAgICAgICAgY3R4LmZvbnQgPSAmcXVvdDszMHB4IHNhbnMtc2VyaWYmcXVvdDs7CiAgICAgICAgICAgICAgICBjdHguZmlsbFRleHQoJnF1b3Q7R0FNRSBPVkVSJnF1b3Q7LCAxNTAsIDEwMCk7CiAgICAgICAgICAgICAgICByZXRyeUJ0bi5zdHlsZS5kaXNwbGF5ID0gJnF1b3Q7YmxvY2smcXVvdDs7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGlmIChvYnMueCArIG9icy53aWR0aCAmbHQ7IHBsYXllci54ICZhbXA7JmFtcDsgIW9icy5wYXNzZWQpIHsKICAgICAgICAgICAgICAgIG9icy5wYXNzZWQgPSB0cnVlOwogICAgICAgICAgICAgICAgc2NvcmUgKz0gMTA7CiAgICAgICAgICAgICAgICBzY29yZUJvYXJkLmlubmVyVGV4dCA9ICZxdW90O1NDT1JFOiAmcXVvdDsgKyBzY29yZTsKICAgICAgICAgICAgICAgIHNwZWVkICs9IDAuMjsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAob2JzLnggJmx0OyAtMjApIG9ic3RhY2xlcy5zcGxpY2UoaSwgMSk7CiAgICAgICAgfQogICAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZShsb29wKTsKICAgIH0KCiAgICBpbml0KCk7CiAgICBzcGF3bk9ic3RhY2xlKCk7Cn0pKCk7CiZsdDsvc2NyaXB0Jmd0OwonJycpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDIuIOOCv+OCpOODlOODs+OCsOOCueODlOODvOODiea4rOWumuOCsuODvOODoAojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpnb29nbGUuY29sYWIub3V0cHV0Lmh0bWwoJycnCiZsdDtkaXYgc3R5bGU9JnF1b3Q7dGV4dC1hbGlnbjogY2VudGVyOyBmb250LWZhbWlseTogc2Fucy1zZXJpZjsgYmFja2dyb3VuZDogIzJjM2U1MDsgY29sb3I6IHdoaXRlOyBwYWRkaW5nOiAyMHB4OyBib3JkZXItcmFkaXVzOiAxMHB4OyB3aWR0aDogNDAwcHg7IG1hcmdpbjogMCBhdXRvIDQwcHggYXV0bzsmcXVvdDsmZ3Q7CiAgICAmbHQ7aDImZ3Q74pGhIOOCv+OCpOODlOODs+OCsOOCsuODvOODoCZsdDsvaDImZ3Q7CiAgICAmbHQ7ZGl2IGlkPSZxdW90O3RpbWVyJnF1b3Q7IHN0eWxlPSZxdW90O2ZvbnQtc2l6ZTogMThweDsgY29sb3I6ICNmMWM0MGY7JnF1b3Q7Jmd0O+aui+OCiuaZgumWkzogMjDnp5ImbHQ7L2RpdiZndDsKICAgICZsdDtkaXYgaWQ9JnF1b3Q7d29yZERpc3BsYXkmcXVvdDsgc3R5bGU9JnF1b3Q7Zm9udC1zaXplOiAzMnB4OyBmb250LXdlaWdodDogYm9sZDsgbWFyZ2luOiAyMHB4IDA7IGxldHRlci1zcGFjaW5nOiAycHg7JnF1b3Q7Jmd0Oy0tLSZsdDsvZGl2Jmd0OwogICAgJmx0O2lucHV0IHR5cGU9JnF1b3Q7dGV4dCZxdW90OyBpZD0mcXVvdDt0eXBlSW5wdXQmcXVvdDsgc3R5bGU9JnF1b3Q7Zm9udC1zaXplOiAyMHB4OyBwYWRkaW5nOiA1cHg7IHdpZHRoOiA4MCU7IHRleHQtYWxpZ246IGNlbnRlcjsmcXVvdDsgYXV0b2NvbXBsZXRlPSZxdW90O29mZiZxdW90OyBkaXNhYmxlZCZndDsKICAgICZsdDtkaXYgaWQ9JnF1b3Q7c2NvcmVEaXNwbGF5JnF1b3Q7IHN0eWxlPSZxdW90O21hcmdpbi10b3A6IDE1cHg7IGZvbnQtc2l6ZTogMThweDsmcXVvdDsmZ3Q744K544Kz44KiOiAwJmx0Oy9kaXYmZ3Q7CiAgICAmbHQ7YnV0dG9uIGlkPSZxdW90O3N0YXJ0QnRuJnF1b3Q7IHN0eWxlPSZxdW90O21hcmdpbi10b3A6IDE1cHg7IHBhZGRpbmc6IDhweCAyMHB4OyBmb250LXNpemU6IDE2cHg7IGJhY2tncm91bmQ6ICMyN2FlNjA7IGNvbG9yOiB3aGl0ZTsgYm9yZGVyOiBub25lOyBib3JkZXItcmFkaXVzOiA1cHg7IGN1cnNvcjogcG9pbnRlcjsmcXVvdDsmZ3Q744K544K/44O844OIJmx0Oy9idXR0b24mZ3Q7CiZsdDsvZGl2Jmd0OwoKJmx0O3NjcmlwdCZndDsKKGZ1bmN0aW9uKCkgewogICAgY29uc3Qgd29yZHMgPSBbJnF1b3Q7cHl0aG9uJnF1b3Q7LCAmcXVvdDtjb2RlJnF1b3Q7LCAmcXVvdDtnb29nbGUmcXVvdDssICZxdW90O2NvbGFiJnF1b3Q7LCAmcXVvdDtub3RlYm9vayZxdW90OywgJnF1b3Q7cHJvZ3JhbSZxdW90OywgJnF1b3Q7c2NyaXB0JnF1b3Q7LCAmcXVvdDthaSZxdW90OywgJnF1b3Q7ZGF0YSZxdW90OywgJnF1b3Q7d2ViJnF1b3Q7LCAmcXVvdDtnYW1lJnF1b3Q7LCAmcXVvdDtrZXlib2FyZCZxdW90O107CiAgICBsZXQgY3VycmVudFdvcmQgPSAmcXVvdDsmcXVvdDs7CiAgICBsZXQgc2NvcmUgPSAwOwogICAgbGV0IHRpbWVMZWZ0ID0gMjA7CiAgICBsZXQgdGltZXJJZCA9IG51bGw7CgogICAgY29uc3Qgd29yZERpc3BsYXkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgmcXVvdDt3b3JkRGlzcGxheSZxdW90Oyk7CiAgICBjb25zdCB0eXBlSW5wdXQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgmcXVvdDt0eXBlSW5wdXQmcXVvdDspOwogICAgY29uc3Qgc2NvcmVEaXNwbGF5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJnF1b3Q7c2NvcmVEaXNwbGF5JnF1b3Q7KTsKICAgIGNvbnN0IHRpbWVyRGlzcGxheSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCZxdW90O3RpbWVyJnF1b3Q7KTsKICAgIGNvbnN0IHN0YXJ0QnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJnF1b3Q7c3RhcnRCdG4mcXVvdDspOwoKICAgIGZ1bmN0aW9uIG5leHRXb3JkKCkgewogICAgICAgIGN1cnJlbnRXb3JkID0gd29yZHNbTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogd29yZHMubGVuZ3RoKV07CiAgICAgICAgd29yZERpc3BsYXkuaW5uZXJUZXh0ID0gY3VycmVudFdvcmQ7CiAgICAgICAgdHlwZUlucHV0LnZhbHVlID0gJnF1b3Q7JnF1b3Q7OwogICAgfQoKICAgIGZ1bmN0aW9uIHN0YXJ0R2FtZSgpIHsKICAgICAgICBzY29yZSA9IDA7CiAgICAgICAgdGltZUxlZnQgPSAyMDsKICAgICAgICBzY29yZURpc3BsYXkuaW5uZXJUZXh0ID0gJnF1b3Q744K544Kz44KiOiAmcXVvdDsgKyBzY29yZTsKICAgICAgICB0aW1lckRpc3BsYXkuaW5uZXJUZXh0ID0gJnF1b3Q75q6L44KK5pmC6ZaTOiAmcXVvdDsgKyB0aW1lTGVmdCArICZxdW90O+enkiZxdW90OzsKICAgICAgICB0eXBlSW5wdXQuZGlzYWJsZWQgPSBmYWxzZTsKICAgICAgICBzdGFydEJ0bi5zdHlsZS5kaXNwbGF5ID0gJnF1b3Q7bm9uZSZxdW90OzsKICAgICAgICBuZXh0V29yZCgpOwogICAgICAgIHR5cGVJbnB1dC5mb2N1cygpOwogICAgICAgIAogICAgICAgIHRpbWVySWQgPSBzZXRJbnRlcnZhbCgoKSA9Jmd0OyB7CiAgICAgICAgICAgIHRpbWVMZWZ0LS07CiAgICAgICAgICAgIHRpbWVyRGlzcGxheS5pbm5lclRleHQgPSAmcXVvdDvmrovjgormmYLplpM6ICZxdW90OyArIHRpbWVMZWZ0ICsgJnF1b3Q756eSJnF1b3Q7OwogICAgICAgICAgICBpZiAodGltZUxlZnQgJmx0Oz0gMCkgewogICAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh0aW1lcklkKTsKICAgICAgICAgICAgICAgIHR5cGVJbnB1dC5kaXNhYmxlZCA9IHRydWU7CiAgICAgICAgICAgICAgICB3b3JkRGlzcGxheS5pbm5lclRleHQgPSAmcXVvdDvntYLkuobvvIEmcXVvdDs7CiAgICAgICAgICAgICAgICBzdGFydEJ0bi5pbm5lclRleHQgPSAmcXVvdDvjgoLjgYbkuIDluqbjg5fjg6zjgqQmcXVvdDs7CiAgICAgICAgICAgICAgICBzdGFydEJ0bi5zdHlsZS5kaXNwbGF5ID0gJnF1b3Q7aW5saW5lLWJsb2NrJnF1b3Q7OwogICAgICAgICAgICB9CiAgICAgICAgfSwgMTAwMCk7CiAgICB9CgogICAgdHlwZUlucHV0LmFkZEV2ZW50TGlzdGVuZXIoJnF1b3Q7aW5wdXQmcXVvdDssICgpID0mZ3Q7IHsKICAgICAgICBpZiAodHlwZUlucHV0LnZhbHVlLnRyaW0oKS50b0xvd2VyQ2FzZSgpID09PSBjdXJyZW50V29yZCkgewogICAgICAgICAgICBzY29yZSsrOwogICAgICAgICAgICBzY29yZURpc3BsYXkuaW5uZXJUZXh0ID0gJnF1b3Q744K544Kz44KiOiAmcXVvdDsgKyBzY29yZTsKICAgICAgICAgICAgbmV4dFdvcmQoKTsKICAgICAgICB9CiAgICB9KTsKCiAgICBzdGFydEJ0bi5hZGRFdmVudExpc3RlbmVyKCZxdW90O2NsaWNrJnF1b3Q7LCBzdGFydEdhbWUpOwp9KSgpOwombHQ7L3NjcmlwdCZndDsKJycnKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAzLiDmlbDlvZPjgabjg63jgrjjg4Pjgq/jgrLjg7zjg6AKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KZGVmIHBsYXlfbnVtZXJpY19nYW1lKCk6CiAgICBwcmludCgmcXVvdDtcbiZxdW90OyArICZxdW90Oz0mcXVvdDsqNDApCiAgICBwcmludCgmcXVvdDvikaIg44CQ5pWw5b2T44Gm44Ot44K444OD44Kv44Ky44O844OgICgz5qGBKeOAkSZxdW90OykKICAgIHByaW50KCZxdW90OzDjgJw544Gu55Ww44Gq44KLM+ahgeOBruaVsOWtl+OCkuW9k+OBpuOBpuOBj+OBoOOBleOBhOOAgiZxdW90OykKICAgIHByaW50KCZxdW90O0VhdCA9IOaVsOWtl+OCguS9jee9ruOCguato+inoyAvIEJpdGUgPSDmlbDlrZfjga/lkIjjgaPjgabjgYTjgovjgYzkvY3nva7jgYzpgZXjgYYmcXVvdDspCiAgICBwcmludCgmcXVvdDs9JnF1b3Q7KjQwICsgJnF1b3Q7XG4mcXVvdDspCiAgICAKICAgIG51bWJlcnMgPSBsaXN0KHJhbmdlKDEwKSkKICAgIHJhbmRvbS5zaHVmZmxlKG51bWJlcnMpCiAgICB0YXJnZXQgPSBudW1iZXJzWzozXQogICAgCiAgICB0dXJucyA9IDAKICAgIHdoaWxlIFRydWU6CiAgICAgICAgdHVybnMgKz0gMQogICAgICAgIGd1ZXNzX3JhdyA9IGlucHV0KGYmcXVvdDtbe3R1cm5z5omL55uuXSAz5qGB44Gu5pWw5a2X44KS5YWl5Yqb44GX44Gm44GP44Gg44GV44GEOiAmcXVvdDspCiAgICAgICAgCiAgICAgICAgaWYgbGVuKGd1ZXNzX3JhdykgIT0gMyBvciBub3QgZ3Vlc3NfcmF3LmlzZGlnaXQoKToKICAgICAgICAgICAgcHJpbnQoJnF1b3Q744Ko44Op44O8OiAz5qGB44Gu5pWw5a2X44KS5q2j56K644KS5YWl5Yqb44GX44Gm44GP44Gg44GV44GE44CCJnF1b3Q7KQogICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICAKICAgICAgICBndWVzcyA9IFtpbnQoY2hhcikgZm9yIGNoYXIgaW4gZ3Vlc3NfcmF3XQogICAgICAgIGlmIGxlbihzZXQoZ3Vlc3MpKSAhPSAzOgogICAgICAgICAgICBwcmludCgmcXVvdDvjgqjjg6njg7w6IOWQjOOBmOaVsOWtl+OCkjLlm57ku6XkuIrkvb/jgYjjgb7jgZvjgpPjgIImcXVvdDspCiAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIAogICAgICAgIGVhdCA9IDAKICAgICAgICBiaXRlID0gMAogICAgICAgIGZvciBpIGluIHJhbmdlKDMpOgogICAgICAgICAgICBpZiBndWVzc1tpXSA9PSB0YXJnZXRbaV06CiAgICAgICAgICAgICAgICBlYXQgKz0gMQogICAgICAgICAgICBlbGlmIGd1ZXNzW2ldIGluIHRhcmdldDoKICAgICAgICAgICAgICAgIGJpdGUgKz0gMQogICAgICAgICAgICAgICAgCiAgICAgICAgcHJpbnQoZiZxdW90O+e1kOaenDoge2VhdH0gRWF0IC8ge2JpdGV9IEJpdGUmcXVvdDspCiAgICAgICAgCiAgICAgICAgaWYgZWF0ID09IDM6CiAgICAgICAgICAgIHByaW50KGYmcXVvdDvwn46JIOOBiuOCgeOBp+OBqOOBhuOBlOOBluOBhOOBvuOBme+8gSB7dHVybnN95omL55uu44Gn5q2j6Kej44GX44G+44GX44Gf77yBJnF1b3Q7KQogICAgICAgICAgICBicmVhawoKcGxheV9udW1lcmljX2dhbWUoKQo=
pythonimport google.colab.output
import random
# ==========================================
# 1. 障害物よけアクションゲーム(キューブラン)
# ==========================================
google.colab.output.html('''
<div style="text-align: center; font-family: sans-serif; margin-bottom: 40px;">
<h3>① キューブラン(クリックでジャンプ!)</h3>
<canvas id="gameCanvas" width="480" height="200" style="border:2px solid #333; background:#f7f7f7;"></canvas>
<div id="scoreBoard" style="font-size: 20px; margin-top: 10px;">SCORE: 0</div>
<button id="retryBtn" style="display:none; margin: 10px auto; padding: 5px 15px; font-size:16px;">もう一度プレイ</button>
</div>
<script>
(function() {
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const scoreBoard = document.getElementById("scoreBoard");
const retryBtn = document.getElementById("retryBtn");
let player, obstacles, score, gameActive, speed;
function init() {
player = { x: 50, y: 150, wy: 0, size: 20, isJumping: false };
obstacles = [];
score = 0;
speed = 4;
gameActive = true;
retryBtn.style.display = "none";
scoreBoard.innerText = "SCORE: 0";
loop();
}
function spawnObstacle() {
if (!gameActive) return;
obstacles.push({ x: canvas.width, y: 150, width: 15, height: 20 + Math.random() * 20 });
setTimeout(spawnObstacle, 1200 + Math.random() * 1000);
}
function jump() {
if (!player.isJumping && gameActive) {
player.wy = -8;
player.isJumping = true;
}
}
canvas.addEventListener("click", jump);
retryBtn.addEventListener("click", init);
function loop() {
if (!gameActive) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#666";
ctx.fillRect(0, 170, canvas.width, 30);
player.y += player.wy;
player.wy += 0.4;
if (player.y >= 150) {
player.y = 150;
player.isJumping = false;
}
ctx.fillStyle = "#3498db";
ctx.fillRect(player.x, player.y, player.size, player.size);
for (let i = obstacles.length - 1; i >= 0; i--) {
let obs = obstacles[i];
obs.x -= speed;
ctx.fillStyle = "#e74c3c";
ctx.fillRect(obs.x, obs.y, obs.width, obs.height);
if (player.x < obs.x + obs.width &&
player.x + player.size > obs.x &&
player.y < obs.y + obs.height &&
player.y + player.size > obs.y) {
gameActive = false;
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#fff";
ctx.font = "30px sans-serif";
ctx.fillText("GAME OVER", 150, 100);
retryBtn.style.display = "block";
}
if (obs.x + obs.width < player.x && !obs.passed) {
obs.passed = true;
score += 10;
scoreBoard.innerText = "SCORE: " + score;
speed += 0.2;
}
if (obs.x < -20) obstacles.splice(i, 1);
}
requestAnimationFrame(loop);
}
init();
spawnObstacle();
})();
</script>
''')
# ==========================================
# 2. タイピングスピード測定ゲーム
# ==========================================
google.colab.output.html('''
<div style="text-align: center; font-family: sans-serif; background: #2c3e50; color: white; padding: 20px; border-radius: 10px; width: 400px; margin: 0 auto 40px auto;">
<h2>② タイピングゲーム</h2>
<div id="timer" style="font-size: 18px; color: #f1c40f;">残り時間: 20秒</div>
<div id="wordDisplay" style="font-size: 32px; font-weight: bold; margin: 20px 0; letter-spacing: 2px;">---</div>
<input type="text" id="typeInput" style="font-size: 20px; padding: 5px; width: 80%; text-align: center;" autocomplete="off" disabled>
<div id="scoreDisplay" style="margin-top: 15px; font-size: 18px;">スコア: 0</div>
<button id="startBtn" style="margin-top: 15px; padding: 8px 20px; font-size: 16px; background: #27ae60; color: white; border: none; border-radius: 5px; cursor: pointer;">スタート</button>
</div>
<script>
(function() {
const words = ["python", "code", "google", "colab", "notebook", "program", "script", "ai", "data", "web", "game", "keyboard"];
let currentWord = "";
let score = 0;
let timeLeft = 20;
let timerId = null;
const wordDisplay = document.getElementById("wordDisplay");
const typeInput = document.getElementById("typeInput");
const scoreDisplay = document.getElementById("scoreDisplay");
const timerDisplay = document.getElementById("timer");
const startBtn = document.getElementById("startBtn");
function nextWord() {
currentWord = words[Math.floor(Math.random() * words.length)];
wordDisplay.innerText = currentWord;
typeInput.value = "";
}
function startGame() {
score = 0;
timeLeft = 20;
scoreDisplay.innerText = "スコア: " + score;
timerDisplay.innerText = "残り時間: " + timeLeft + "秒";
typeInput.disabled = false;
startBtn.style.display = "none";
nextWord();
typeInput.focus();
timerId = setInterval(() => {
timeLeft--;
timerDisplay.innerText = "残り時間: " + timeLeft + "秒";
if (timeLeft <= 0) {
clearInterval(timerId);
typeInput.disabled = true;
wordDisplay.innerText = "終了!";
startBtn.innerText = "もう一度プレイ";
startBtn.style.display = "inline-block";
}
}, 1000);
}
typeInput.addEventListener("input", () => {
if (typeInput.value.trim().toLowerCase() === currentWord) {
score++;
scoreDisplay.innerText = "スコア: " + score;
nextWord();
}
});
startBtn.addEventListener("click", startGame);
})();
</script>
''')
# ==========================================
# 3. 数当てロジックゲーム
# ==========================================
def play_numeric_game():
print("\n" + "="*40)
print("③ 【数当てロジックゲーム (3桁)】")
print("0〜9の異なる3桁の数字を当ててください。")
print("Eat = 数字も位置も正解 / Bite = 数字は合っているが位置が違う")
print("="*40 + "\n")
numbers = list(range(10))
random.shuffle(numbers)
target = numbers[:3]
turns = 0
while True:
turns += 1
guess_raw = input(f"[{turns手目] 3桁の数字を入力してください: ")
if len(guess_raw) != 3 or not guess_raw.isdigit():
print("エラー: 3桁の数字を正確を入力してください。")
continue
guess = [int(char) for char in guess_raw]
if len(set(guess)) != 3:
print("エラー: 同じ数字を2回以上使えません。")
continue
eat = 0
bite = 0
for i in range(3):
if guess[i] == target[i]:
eat += 1
elif guess[i] in target:
bite += 1
print(f"結果: {eat} Eat / {bite} Bite")
if eat == 3:
print(f"🎉 おめでとうございます! {turns}手目で正解しました!")
break
play_numeric_game()