{"id":474850,"date":"2025-09-12T21:10:20","date_gmt":"2025-09-12T21:10:20","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=474850"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=474850","title":{"rendered":"<span>\u041d\u0435\u0443\u0434\u0430\u0447\u043d\u044b\u0435 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u044b \u0441 Vibe Coding \u043d\u0430 Python<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041a\u043e\u043c\u0430\u043d\u0434\u0430 <a href=\"https:\/\/t.me\/+wyxutDzVYHcxNzE6\" rel=\"noopener noreferrer nofollow\">Python for Devs<\/a> \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0441\u0442\u0430\u0442\u044c\u0438 \u042d\u043ba \u0421\u0432\u0435\u0439\u0433\u0430\u0440\u0442\u0430 \u043e \u043d\u0435\u0443\u0434\u0430\u0447\u043d\u044b\u0445 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u0445 \u0441 vibe coding. \u0412\u0441\u0435 \u0433\u043e\u0432\u043e\u0440\u044f\u0442, \u0447\u0442\u043e \u0418\u0418 \u0443\u0436\u0435 \u0443\u043c\u0435\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u0441\u0442\u043e\u0438\u0442 \u0447\u0443\u0442\u044c \u043e\u0442\u043a\u043b\u043e\u043d\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u2014 \u0438 \u0432\u0441\u0451 \u0438\u0434\u0451\u0442 \u043d\u0430\u043f\u0435\u0440\u0435\u043a\u043e\u0441\u044f\u043a. \u041a\u0430\u0440\u0442\u043e\u0444\u0435\u043b\u044c\u043d\u0430\u044f \u0410\u0444\u0440\u0438\u043a\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u043a\u0430\u0440\u0442\u044b, \u043f\u0438\u043d\u0431\u043e\u043b, \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439\u0441\u044f \u0432 \u043f\u0438\u043d\u0433-\u043f\u043e\u043d\u0433, \u0438 \u0441\u0447\u0451\u0442\u044b \u0441 \u043e\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0447\u0438\u0441\u043b\u0430\u043c\u0438 \u2014 \u0430\u0432\u0442\u043e\u0440 \u0441\u043e\u0431\u0440\u0430\u043b \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u043e\u0432\u0430\u043b\u043e\u0432 \u0441 vibe coding.<\/p>\n<hr\/>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u043d\u0435\u0434\u0435\u043b\u044e \u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043b \u0441 vibe coding: \u043f\u0440\u043e\u0441\u0438\u043b LLM-\u043c\u043e\u0434\u0435\u043b\u0438 \u0432\u0440\u043e\u0434\u0435 ChatGPT, Claude \u0438 Gemini \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0442\u0430\u043a, \u0431\u0443\u0434\u0442\u043e \u0443 \u043c\u0435\u043d\u044f \u043d\u0435\u0442 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043d\u0430\u0432\u044b\u043a\u043e\u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. LLM \u043b\u0435\u0433\u043a\u043e \u0440\u0435\u0448\u0430\u044e\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0430 \u043a\u043e\u0434\u0438\u043d\u0433 \u0438\u043b\u0438 \u0441\u043e\u0431\u0435\u0441\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b. \u041d\u043e \u043c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u0430\u043b\u0435\u043a\u043e \u043e\u043d\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b \u0437\u0430\u0439\u0442\u0438, \u0435\u0441\u043b\u0438 \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0438\u0445 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0438 \u043a\u0430\u043a\u0438\u0435 \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0435 \u0441\u0431\u043e\u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f. \u0412 \u0440\u043e\u043b\u0438 \u00ab\u043d\u0435\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u00bb \u044f \u043c\u043e\u0433 \u0431\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0431\u0430\u0433\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044f \u0438\u0445 LLM. \u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u044f \u0432\u044b\u0431\u0440\u0430\u043b \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 Python, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0438 \u043f\u0430\u043a\u0435\u0442 <code>tkinter<\/code> \u0434\u043b\u044f GUI. \u0412 \u044d\u0442\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u043e\u0431 \u044d\u0442\u0438\u0445 \u043f\u0440\u043e\u0432\u0430\u043b\u0430\u0445 \u2014 \u043e \u0442\u0435\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445, \u0433\u0434\u0435 \u0418\u0418 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f.<\/p>\n<p>\u041c\u0435\u043d\u044f \u043d\u0435 \u0432\u043e\u043b\u043d\u0443\u0435\u0442 \u0438\u0437\u044b\u0441\u043a\u0430\u043d\u043d\u044b\u0439 \u0438\u043b\u0438 \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 (\u0432 \u043a\u043e\u043d\u0446\u0435 \u043a\u043e\u043d\u0446\u043e\u0432, \u0442\u0443\u0442 \u0432\u0441\u0451 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043e tkinter). \u0412\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0435\u0437 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a. \u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u0432 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b ChatGPT 5, Gemini 2.5 Pro \u0438 Claude Sonnet 4.<\/p>\n<p>\u0412 \u0442\u0435\u043a\u0441\u0442\u0435 \u044f \u043f\u0440\u0438\u0432\u043e\u0436\u0443 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c, \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 LLM. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u0443\u0434\u0430\u0441\u0442\u0441\u044f \u0434\u043e\u0432\u0435\u0441\u0442\u0438 \u0434\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043b\u044e\u0431\u0443\u044e \u0438\u0437 \u044d\u0442\u0438\u0445 \u0438\u0434\u0435\u0439, \u043c\u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445:\u00a0<a href=\"mailto:al@inventwithpython.com\" rel=\"noopener noreferrer nofollow\">al@inventwithpython.com<\/a>.<\/p>\n<h2>\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u043d\u0435\u0443\u0434\u0430\u0447 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u043e\u0442 LLM<\/h2>\n<p>\u041e\u0431\u044b\u0447\u043d\u043e LLM \u043d\u0435 \u0443\u0434\u0430\u0432\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u043e\u0444\u0442 \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438:<\/p>\n<ul>\n<li>\n<p>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043d\u0435\u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u041b\u044e\u0431\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043d\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0442\u043d\u0438 \u0440\u0430\u0437 (\u0422\u0435\u0442\u0440\u0438\u0441, \u0441\u0435\u043a\u0443\u043d\u0434\u043e\u043c\u0435\u0440, \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u043b \u0438 \u0442. \u043f.).<\/p>\n<\/li>\n<li>\n<p>\u0422\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0438\u043b\u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a. LLM \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 \u0442\u0435\u043a\u0441\u0442, \u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438 \u0438\u043b\u0438 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u043e\u0439 \u0443 \u043d\u0438\u0445 \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0430\u0437\u0432\u0430\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0445\u043e\u0436\u0438\u0435, \u043d\u043e \u043d\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u044b\u0435 \u0442\u0438\u043f\u043e\u0432\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0438\u043d\u0431\u043e\u043b \u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u043f\u0438\u043d\u0433-\u043f\u043e\u043d\u0433. \u0415\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u044b \u0430\u043c\u043e\u0440\u0444\u043d\u044b\u0435 \u043f\u044f\u0442\u043d\u0430, \u043a\u0430\u043a \u0432 \u043b\u0430\u0432\u043e\u0432\u043e\u0439 \u043b\u0430\u043c\u043f\u0435, LLM \u0440\u0438\u0441\u0443\u044e\u0442 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u0440\u0443\u0433\u0438. \u041c\u043e\u0434\u0435\u043b\u0438 \u0441\u043a\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043a \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u043c, \u043d\u043e \u043d\u0435\u0442\u043e\u0447\u043d\u044b\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c, \u0438\u043d\u043e\u0433\u0434\u0430 \u0434\u0430\u0436\u0435 \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u043f\u0440\u044f\u043c\u044b\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e\u0433\u043e.<\/p>\n<\/li>\n<\/ul>\n<h2>\u0421\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u0432 \u0441 vibe coding:<\/h2>\n<ul>\n<li>\n<p>\u0412\u0438\u043a\u0442\u043e\u0440\u0438\u043d\u0430 \u043f\u043e \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0441\u0442\u0440\u0430\u043d \u0410\u0444\u0440\u0438\u043a\u0438<\/p>\n<\/li>\n<li>\n<p>\u0418\u0433\u0440\u0430 \u00ab\u041f\u0438\u043d\u0431\u043e\u043b\u00bb<\/p>\n<\/li>\n<li>\n<p>\u0413\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u043a\u0440\u0443\u0433\u043e\u0432\u044b\u0445 \u043b\u0430\u0431\u0438\u0440\u0438\u043d\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0418\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u0438\u0442\u0430\u0439\u0441\u043a\u0438\u0435 \u0441\u0447\u0435\u0442\u044b<\/p>\n<\/li>\n<li>\n<p>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u043a\u043e\u0434\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043c\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0433\u0435\u043d\u0435\u0430\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0435\u0440\u0435\u0432\u044c\u0435\u0432<\/p>\n<\/li>\n<li>\n<p>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u043b\u0430\u0432\u043e\u0432\u043e\u0439 \u043b\u0430\u043c\u043f\u044b<\/p>\n<\/li>\n<li>\n<p>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u0441\u043d\u0435\u0436\u043d\u043e\u0433\u043e \u0448\u0430\u0440\u0430<\/p>\n<\/li>\n<\/ul>\n<h2>\u0412\u0438\u043a\u0442\u043e\u0440\u0438\u043d\u0430 \u043f\u043e \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0441\u0442\u0440\u0430\u043d \u0410\u0444\u0440\u0438\u043a\u0438<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442:<\/p>\n<blockquote>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043d\u0430 Python \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c tkinter \u0434\u043b\u044f GUI \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0438\u0437 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0410\u0444\u0440\u0438\u043a\u0438 \u0431\u0435\u0437 \u0433\u0440\u0430\u043d\u0438\u0446 \u043c\u0435\u0436\u0434\u0443 \u0441\u0442\u0440\u0430\u043d\u0430\u043c\u0438, \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0442\u0443\u0440 \u0410\u0444\u0440\u0438\u043a\u0438. \u041e\u043d\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0430\u0444\u0440\u0438\u043a\u0430\u043d\u0441\u043a\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u044b, \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u0435\u043d \u043a\u043b\u0438\u043a\u043d\u0443\u0442\u044c \u043f\u043e \u043a\u0430\u0440\u0442\u0435. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0443\u0440 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u044b \u043d\u0430 \u043a\u0430\u0440\u0442\u0435 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434, \u043f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u044b. \u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043d\u0435\u0442 \u043e\u0447\u043a\u043e\u0432 \u0438\u043b\u0438 \u0431\u0430\u043b\u043b\u043e\u0432. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442, \u043f\u043e\u043f\u0430\u043b \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0441\u0442\u0440\u0430\u043d\u044b, \u0438\u043b\u0438 \u043d\u0435\u0442 (\u043e\u043d \u0438 \u0442\u0430\u043a \u0443\u0432\u0438\u0434\u0438\u0442 \u044d\u0442\u043e, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0443\u0440 \u0441\u0442\u0440\u0430\u043d\u044b). \u0427\u0442\u043e\u0431\u044b \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c, \u0442\u043e\u0447\u043a\u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043d\u0435\u0442: \u0432\u0438\u043a\u0442\u043e\u0440\u0438\u043d\u0430 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0437\u0430\u043a\u0440\u043e\u0435\u0442 \u043e\u043a\u043d\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: LLM \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u00ab\u0437\u0430\u0431\u044b\u0432\u0430\u044e\u0442\u00bb, \u0447\u0442\u043e \u041c\u0430\u0434\u0430\u0433\u0430\u0441\u043a\u0430\u0440 \u2014 \u0447\u0430\u0441\u0442\u044c \u0410\u0444\u0440\u0438\u043a\u0438, \u0435\u0441\u043b\u0438 \u0438\u043c \u043e\u0431 \u044d\u0442\u043e\u043c \u043d\u0435 \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c. \u0415\u0449\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e LLM \u0441\u0447\u0438\u0442\u0430\u044e\u0442, \u0431\u0443\u0434\u0442\u043e \u0410\u0444\u0440\u0438\u043a\u0430 \u043f\u043e \u0444\u043e\u0440\u043c\u0435 \u043a\u0430\u043a \u043a\u0430\u0440\u0442\u043e\u0448\u043a\u0430.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/969\/e08\/425\/969e08425bf7047ed33693fc2653f2a2.png\" width=\"2170\" height=\"800\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/969\/e08\/425\/969e08425bf7047ed33693fc2653f2a2.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/969\/e08\/425\/969e08425bf7047ed33693fc2653f2a2.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u042f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u043b \u043d\u0430 \u043d\u0435\u0447\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043d\u0430 <a href=\"https:\/\/www.sporcle.com\/games\/kfastic\/countries-of-africa-without-outlines\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u0443 \u0432\u0438\u043a\u0442\u043e\u0440\u0438\u043d\u0443 \u043f\u043e \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0438<\/a>: \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0410\u0444\u0440\u0438\u043a\u0430 \u0431\u0435\u0437 \u0433\u0440\u0430\u043d\u0438\u0446 \u0441\u0442\u0440\u0430\u043d, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e\u0442 \u043a\u043b\u0438\u043a\u043d\u0443\u0442\u044c \u043f\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0435.<\/p>\n<p>\u041e\u0434\u043d\u0430 \u0438\u0437 LLM \u0432\u044b\u0434\u0430\u043b\u0430 \u0447\u0442\u043e-\u0442\u043e \u043e\u0442\u0434\u0430\u043b\u0435\u043d\u043d\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043d\u0430 \u0410\u0444\u0440\u0438\u043a\u0443 \u0438 \u0434\u0430\u0436\u0435 \u043d\u0430\u0440\u0438\u0441\u043e\u0432\u0430\u043b\u0430 \u041c\u0430\u0434\u0430\u0433\u0430\u0441\u043a\u0430\u0440 (\u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u044f \u043d\u0430\u043f\u043e\u043c\u043d\u0438\u043b, \u0447\u0442\u043e \u043e\u043d \u2014 \u0447\u0430\u0441\u0442\u044c \u0410\u0444\u0440\u0438\u043a\u0438). \u041d\u043e \u0444\u043e\u0440\u043c\u044b \u0441\u0442\u0440\u0430\u043d \u0431\u044b\u043b\u0438&#8230; \u043c\u044f\u0433\u043a\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u043d\u0435\u0442\u043e\u0447\u043d\u044b\u043c\u0438. \u042f \u043f\u0440\u043e\u0441\u0438\u043b \u0438\u0445 \u043d\u0430\u0439\u0442\u0438 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 SVG-\u0444\u0430\u0439\u043b\u044b \u043a\u0430\u0440\u0442 \u0410\u0444\u0440\u0438\u043a\u0438. \u041e\u043d\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u043b\u0438, \u0447\u0442\u043e \u043d\u0430\u0448\u043b\u0438, \u0430 \u0437\u0430\u0442\u0435\u043c \u0441\u043d\u043e\u0432\u0430 \u0440\u0438\u0441\u043e\u0432\u0430\u043b\u0438 \u043a\u0430\u0440\u0442\u043e\u0448\u043a\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>fail_africa_1.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk from tkinter import ttk import random import math  class AfricaGeographyQuiz:     def __init__(self):         self.root = tk.Tk()         self.root.title(\"African Geography Quiz\")         self.root.geometry(\"800x700\")         self.root.configure(bg='lightblue')                  # African countries and their approximate center coordinates (normalized 0-1)         self.countries = {             'Algeria': (0.5, 0.25),             'Angola': (0.35, 0.75),             'Benin': (0.45, 0.55),             'Botswana': (0.55, 0.8),             'Burkina Faso': (0.4, 0.5),             'Burundi': (0.65, 0.7),             'Cameroon': (0.5, 0.6),             'Cape Verde': (0.15, 0.45),             'Central African Republic': (0.55, 0.6),             'Chad': (0.55, 0.5),             'Comoros': (0.8, 0.75),             'Democratic Republic of the Congo': (0.55, 0.7),             'Republic of the Congo': (0.5, 0.65),             'Djibouti': (0.8, 0.5),             'Egypt': (0.65, 0.25),             'Equatorial Guinea': (0.48, 0.6),             'Eritrea': (0.75, 0.45),             'Eswatini': (0.65, 0.85),             'Ethiopia': (0.75, 0.55),             'Gabon': (0.48, 0.65),             'Gambia': (0.25, 0.5),             'Ghana': (0.4, 0.55),             'Guinea': (0.3, 0.5),             'Guinea-Bissau': (0.25, 0.5),             'Ivory Coast': (0.35, 0.55),             'Kenya': (0.75, 0.65),             'Lesotho': (0.6, 0.85),             'Liberia': (0.3, 0.55),             'Libya': (0.55, 0.3),             'Madagascar': (0.85, 0.8),             'Malawi': (0.7, 0.75),             'Mali': (0.4, 0.45),             'Mauritania': (0.3, 0.4),             'Mauritius': (0.9, 0.8),             'Morocco': (0.35, 0.2),             'Mozambique': (0.7, 0.8),             'Namibia': (0.5, 0.8),             'Niger': (0.5, 0.45),             'Nigeria': (0.5, 0.55),             'Rwanda': (0.65, 0.7),             'Sao Tome and Principe': (0.45, 0.6),             'Senegal': (0.25, 0.45),             'Seychelles': (0.9, 0.7),             'Sierra Leone': (0.28, 0.55),             'Somalia': (0.85, 0.6),             'South Africa': (0.6, 0.85),             'South Sudan': (0.65, 0.55),             'Sudan': (0.65, 0.45),             'Tanzania': (0.7, 0.7),             'Togo': (0.43, 0.55),             'Tunisia': (0.5, 0.2),             'Uganda': (0.7, 0.65),             'Zambia': (0.6, 0.75),             'Zimbabwe': (0.65, 0.8)         }                  self.current_country = None         self.showing_answer = False                  self.setup_ui()         self.next_question()              def setup_ui(self):         # Title         title_label = tk.Label(self.root, text=\"African Geography Quiz\",                                font=('Arial', 20, 'bold'), bg='lightblue', fg='darkblue')         title_label.pack(pady=10)                  # Instructions         instructions = tk.Label(self.root,                                 text=\"Click on the map where you think the country is located!\",                                 font=('Arial', 12), bg='lightblue', fg='darkgreen')         instructions.pack(pady=5)                  # Country prompt         self.country_label = tk.Label(self.root, text=\"\",                                       font=('Arial', 16, 'bold'), bg='lightblue', fg='red')         self.country_label.pack(pady=10)                  # Canvas for the map         self.canvas = tk.Canvas(self.root, width=600, height=500, bg='lightcyan')         self.canvas.pack(pady=10)         self.canvas.bind(\"&lt;Button-1&gt;\", self.on_map_click)                  # Draw Africa outline         self.draw_africa_outline()              def draw_africa_outline(self):         \"\"\"Draw a simplified outline of Africa\"\"\"         # Simplified Africa outline coordinates (scaled to canvas)         africa_points = [             # Starting from northwest, going clockwise             100, 150,  # Morocco area             120, 140,  # North Morocco             140, 130,  # Algeria             200, 120,  # Tunisia             250, 130,  # Libya             350, 140,  # Egypt             370, 160,  # Egypt east             380, 200,  # Red Sea             390, 250,  # Sudan\/Eritrea             400, 300,  # Ethiopia             420, 350,  # Somalia             430, 400,  # Kenya             440, 450,  # Tanzania             450, 480,  # Mozambique north             460, 500,  # Mozambique             450, 520,  # South Africa east             400, 530,  # South Africa             350, 525,  # Lesotho area             300, 520,  # South Africa west             250, 510,  # Namibia             200, 500,  # Angola             150, 480,  # Angola north             120, 450,  # DRC             100, 400,  # Cameroon             90, 350,   # Nigeria             85, 300,   # Niger             80, 250,   # Mali             75, 200,   # Mauritania             80, 180,   # Morocco west             100, 150   # Back to start         ]                  self.canvas.create_polygon(africa_points, outline='black', fill='tan', width=2)              def next_question(self):         \"\"\"Select a random country for the next question\"\"\"         if self.showing_answer:             return                      self.current_country = random.choice(list(self.countries.keys()))         self.country_label.config(text=f\"Find: {self.current_country}\")                  # Clear any previous answer markers         self.canvas.delete(\"answer_marker\")              def on_map_click(self, event):         \"\"\"Handle clicks on the map\"\"\"         if self.showing_answer:             return                      # Show where the user clicked         self.canvas.create_oval(event.x-5, event.y-5, event.x+5, event.y+5,                                 fill='blue', outline='darkblue', width=2, tags=\"click_marker\")                  # Show the correct location         self.show_answer()              def show_answer(self):         \"\"\"Show the correct location of the country\"\"\"         self.showing_answer = True                  # Get the country's coordinates and convert to canvas coordinates         country_coords = self.countries[self.current_country]         canvas_x = country_coords[0] * 600  # Scale to canvas width         canvas_y = country_coords[1] * 500  # Scale to canvas height                  # Adjust coordinates to fit within Africa outline         canvas_x = max(75, min(525, canvas_x))         canvas_y = max(120, min(480, canvas_y))                  # Draw the correct location marker         self.canvas.create_oval(canvas_x-8, canvas_y-8, canvas_x+8, canvas_y+8,                                fill='red', outline='darkred', width=3, tags=\"answer_marker\")                  # Draw country name near the marker         self.canvas.create_text(canvas_x, canvas_y-20, text=self.current_country,                                font=('Arial', 10, 'bold'), fill='darkred', tags=\"answer_marker\")                  # Schedule next question         self.root.after(3000, self.prepare_next_question)              def prepare_next_question(self):         \"\"\"Prepare for the next question\"\"\"         self.showing_answer = False         # Clear click markers         self.canvas.delete(\"click_marker\")         # Answer markers will be cleared in next_question         self.next_question()              def run(self):         \"\"\"Start the application\"\"\"         self.root.mainloop()  if __name__ == \"__main__\":     app = AfricaGeographyQuiz()     app.run()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_africa_2.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import random  # --- High-Resolution Coordinate Data --- # This data provides more detailed outlines for the continent and countries. # Coordinates are in (longitude, latitude) format. AFRICA_DATA = {     \"Algeria\": [[(8.68, 28.97), (8.68, 36.8), (3.28, 36.8), (-1.9, 35.9), (-8.67, 30.9), (-8.67, 21.5), (-4.2, 25.0), (2.4, 23.4), (5.5, 19.3), (11.9, 23.9), (9.5, 28.0), (8.68, 28.97)]],     \"Angola\": [[(13.2, -4.8), (11.7, -17.9), (24.0, -17.9), (24.0, -10.7), (22.0, -11.2), (21.8, -4.8), (16.5, -5.9), (13.2, -4.8)]],     \"Botswana\": [[(20.0, -17.7), (20.0, -26.9), (25.3, -25.8), (29.4, -22.1), (26.7, -20.2), (20.0, -17.7)]],     \"Cameroon\": [[(8.4, 2.2), (16.2, 2.2), (16.2, 13.1), (12.5, 10.5), (10.5, 11.5), (9.3, 9.5), (8.4, 4.5), (8.4, 2.2)]],     \"Chad\": [[(13.4, 7.4), (24.0, 7.4), (24.0, 19.5), (20.0, 23.4), (15.0, 23.4), (14.3, 12.8), (13.4, 7.4)]],     \"Democratic Republic of the Congo\": [[(12.2, -5.8), (12.2, -13.4), (29.4, -13.4), (31.2, -8.7), (29.5, -2.5), (30.8, 1.2), (30.0, 4.2), (18.5, 5.4), (16.7, 2.0), (12.2, -5.8)]],     \"Egypt\": [[(25.0, 22.0), (25.0, 31.6), (34.0, 31.6), (36.9, 27.8), (34.8, 22.0), (25.0, 22.0)]],     \"Ethiopia\": [[(33.0, 3.4), (44.0, 3.4), (48.0, 8.0), (43.0, 11.0), (42.2, 14.8), (36.3, 14.8), (35.0, 12.5), (33.0, 6.0), (33.0, 3.4)]],     \"Kenya\": [[(33.9, -4.7), (40.9, -4.7), (41.9, -1.8), (41.0, 4.6), (34.5, 4.6), (34.0, 0.0), (33.9, -4.7)]],     \"Libya\": [[(9.4, 19.5), (25.0, 19.5), (25.0, 31.6), (11.5, 33.0), (9.4, 29.0), (9.4, 19.5)]],     \"Madagascar\": [[(43.2, -12.0), (50.5, -15.4), (49.5, -25.6), (43.7, -25.1), (43.2, -12.0)]],     \"Mali\": [[(-12.2, 10.1), (4.2, 10.1), (4.2, 25.0), (-5.0, 25.0), (-11.5, 14.8), (-12.2, 10.1)]],     \"Morocco\": [[(-13.1, 27.6), (-8.6, 27.6), (-8.6, 29.0), (-1.0, 32.0), (-1.0, 35.9), (-5.9, 35.9), (-8.8, 33.5), (-13.1, 27.6)]],     \"Mozambique\": [[(30.2, -26.8), (40.8, -10.4), (35.0, -10.4), (32.5, -16.0), (30.2, -22.0), (30.2, -26.8)]],     \"Namibia\": [[(11.7, -16.9), (11.7, -28.9), (20.0, -28.9), (20.0, -22.0), (25.2, -17.9), (23.0, -16.9), (11.7, -16.9)]],     \"Niger\": [[(0.1, 11.9), (16.0, 11.9), (16.0, 20.2), (12.0, 23.5), (5.0, 23.5), (3.0, 16.0), (0.1, 11.9)]],     \"Nigeria\": [[(2.6, 4.2), (14.6, 4.2), (14.6, 13.8), (4.0, 13.8), (2.6, 6.4), (2.6, 4.2)]],     \"Somalia\": [[(41.0, -1.7), (51.4, 10.0), (51.0, 12.0), (49.0, 11.8), (41.5, 1.8), (41.0, -1.7)]],     \"South Africa\": [[(16.4, -28.5), (16.4, -34.8), (28.0, -34.8), (32.9, -26.8), (31.0, -22.1), (22.0, -22.1), (20.0, -24.7), (16.4, -28.5)]],     \"Sudan\": [[(21.8, 9.7), (38.6, 9.7), (38.6, 18.0), (35.0, 22.0), (25.0, 22.0), (21.8, 12.0), (21.8, 9.7)]],     \"Tanzania\": [[(29.6, -11.7), (40.4, -4.5), (39.0, -1.0), (30.5, -1.0), (29.6, -8.3), (29.6, -11.7)]],     \"Zambia\": [[(22.0, -18.0), (33.7, -8.2), (28.7, -8.2), (25.0, -12.0), (22.0, -16.0), (22.0, -18.0)]],     \"Zimbabwe\": [[(25.2, -22.4), (33.0, -22.4), (33.0, -15.6), (27.0, -15.6), (25.2, -18.0), (25.2, -22.4)]], }  # Outline of the African continent, including Madagascar AFRICA_OUTLINE = [     # Mainland     [(-17.5, 14.8), (-16.5, 12.0), (-13.0, 7.5), (-8.0, 4.5), (1.0, 4.5), (8.5, 4.5), (9.5, 1.5), (10.0, -1.0), (12.0, -6.0), (13.0, -10.0), (15.0, -18.0), (18.4, -28.7), (20.0, -34.8), (25.0, -34.8), (32.8, -28.0), (40.8, -10.4), (44.0, -1.0), (51.4, 11.8), (45.0, 15.0), (39.6, 15.8), (34.0, 22.0), (35.8, 30.0), (25.0, 31.5), (10.0, 33.0), (10.2, 37.3), (-5.4, 34.6), (-5.9, 35.9), (-8.8, 33.5), (-13.1, 27.6), (-17.5, 20.9), (-17.5, 14.8)],     # Madagascar     [(43.2, -12.0), (50.5, -15.4), (49.5, -25.6), (43.7, -25.1), (43.2, -12.0)] ]  class AfricaQuizApp:     \"\"\"A simple geography quiz application for Africa.\"\"\"      def __init__(self, root):         \"\"\"Initialize the application.\"\"\"         self.root = root         self.root.title(\"Africa Geography Quiz\")         self.root.configure(bg=\"#f0f0f0\")          # --- Configuration ---         self.canvas_width = 600         self.canvas_height = 650         self.map_padding = 20         self.country_fill_color = \"#3498db\" # A nice blue         self.africa_outline_color = \"#555555\"         self.africa_fill_color = \"#e0e0e0\"         self.reveal_duration_ms = 2000 # 2 seconds          # --- State Variables ---         self.current_country = None         self.country_polygon_ids = [] # Store multiple polygon IDs         self.country_list = list(AFRICA_DATA.keys())          # --- UI Elements ---         self.info_label = tk.Label(             self.root,             text=\"Find this country:\",             font=(\"Helvetica\", 14),             bg=\"#f0f0f0\"         )         self.info_label.pack(pady=(10, 0))          self.country_label = tk.Label(             self.root,             text=\"\",             font=(\"Helvetica\", 20, \"bold\"),             bg=\"#f0f0f0\",             fg=\"#2c3e50\"         )         self.country_label.pack(pady=(0, 10))          self.canvas = tk.Canvas(             self.root,             width=self.canvas_width,             height=self.canvas_height,             bg=\"white\",             highlightthickness=0         )         self.canvas.pack(padx=20, pady=(0, 20))         self.canvas.bind(\"&lt;Button-1&gt;\", self.on_map_click)          # --- Setup ---         self.calculate_map_bounds()         self.draw_africa()         self.next_country()      def calculate_map_bounds(self):         \"\"\"Calculate the bounding box of all coordinates to scale the map.\"\"\"         all_points = []         for part in AFRICA_OUTLINE:             all_points.extend(part)          for country_polygons in AFRICA_DATA.values():             for polygon in country_polygons:                 all_points.extend(polygon)          longitudes = [p[0] for p in all_points]         latitudes = [p[1] for p in all_points]          self.min_lon = min(longitudes)         self.max_lon = max(longitudes)         self.min_lat = min(latitudes)         self.max_lat = max(latitudes)          # Calculate scale and offset to fit map in canvas         lon_range = self.max_lon - self.min_lon         lat_range = self.max_lat - self.min_lat          scale_x = (self.canvas_width - 2 * self.map_padding) \/ lon_range         scale_y = (self.canvas_height - 2 * self.map_padding) \/ lat_range         self.scale = min(scale_x, scale_y) * 0.98 # Add a small margin          # Calculate offsets to center the map         map_width = lon_range * self.scale         map_height = lat_range * self.scale         self.x_offset = (self.canvas_width - map_width) \/ 2         self.y_offset = (self.canvas_height - map_height) \/ 2       def transform_coords(self, lon, lat):         \"\"\"Convert longitude and latitude to canvas coordinates.\"\"\"         x = (lon - self.min_lon) * self.scale + self.x_offset         # Invert Y because canvas (0,0) is top-left         y = self.canvas_height - ((lat - self.min_lat) * self.scale + self.y_offset)         return x, y      def draw_africa(self):         \"\"\"Draw the main outline of Africa on the canvas.\"\"\"         for part in AFRICA_OUTLINE:             transformed_points = [self.transform_coords(lon, lat) for lon, lat in part]             self.canvas.create_polygon(                 transformed_points,                 outline=self.africa_outline_color,                 fill=self.africa_fill_color,                 width=2             )      def next_country(self):         \"\"\"Selects and displays the next random country.\"\"\"         # Clear the previously shown country polygon(s) if they exist         for polygon_id in self.country_polygon_ids:             self.canvas.delete(polygon_id)         self.country_polygon_ids = []          # Pick a new random country         self.current_country = random.choice(self.country_list)         self.country_label.config(text=self.current_country)      def on_map_click(self, event):         \"\"\"Handles the user clicking on the map.\"\"\"         # Prevent multiple clicks while a country is being shown         if self.country_polygon_ids:             return          self.show_country_outline()         # Schedule the next country prompt after a delay         self.root.after(self.reveal_duration_ms, self.next_country)      def show_country_outline(self):         \"\"\"Draws the outline of the current country.\"\"\"         if self.current_country:             country_polygons = AFRICA_DATA[self.current_country]              for polygon in country_polygons:                 transformed_points = [self.transform_coords(lon, lat) for lon, lat in polygon]                  # Draw each polygon part of the country and store its ID                 polygon_id = self.canvas.create_polygon(                     transformed_points,                     fill=self.country_fill_color,                     outline=\"white\",                     width=1                 )                 self.country_polygon_ids.append(polygon_id)   if __name__ == \"__main__\":     root = tk.Tk()     app = AfricaQuizApp(root)     root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_africa_3.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import random import time import threading  # Very simplified Africa outline coordinates (replace with better outline if desired): AFRICA_OUTLINE = [     (50, 20), (100, 10), (160, 15), (200, 40), (240, 80),     (250, 130), (230, 180), (210, 220), (170, 260), (120, 280),     (80, 300), (60, 260), (50, 200), (40, 150), (40, 80) ]  # Dictionary mapping country names to rough polygon coordinates. # (These are approximate positions and sizes \u00e2\u20ac\u201c refine or expand as needed.) COUNTRY_COORDS = {     \"Egypt\": [(160, 40), (200, 45), (200, 75), (160, 70)],     \"Nigeria\": [(110, 145), (140, 145), (140, 175), (110, 175)],     \"South Africa\": [(90, 230), (130, 230), (130, 260), (90, 260)],     \"Kenya\": [(165, 130), (190, 130), (190, 160), (165, 160)],     \"Morocco\": [(75, 50), (100, 50), (100, 80), (75, 80)],     \"Madagascar\": [(245, 200), (265, 200), (265, 250), (245, 250)] }  COUNTRY_LIST = list(COUNTRY_COORDS.keys())   class AfricaQuizApp:     def __init__(self, root):         self.root = root         self.root.title(\"Africa Geography Quiz\")          self.canvas = tk.Canvas(root, width=300, height=320)         self.canvas.pack()          # Draw Africa outline         self.canvas.create_polygon(AFRICA_OUTLINE, outline=\"black\", fill=\"white\")          self.label = tk.Label(root, text=\"\", font=(\"Arial\", 14))         self.label.pack(pady=8)          self.current_country = None         self.country_outline = None          self.canvas.bind(\"&lt;Button-1&gt;\", self.on_click)          self.next_country()      def on_click(self, event):         # Draw country outline regardless of whether click was \"correct\"         if self.country_outline is not None:             self.canvas.delete(self.country_outline)          coords = COUNTRY_COORDS[self.current_country]         self.country_outline = self.canvas.create_polygon(             coords, outline=\"red\", fill=\"\", width=2         )         # Start a thread so the UI does not freeze         threading.Thread(target=self.show_then_next).start()      def show_then_next(self):         time.sleep(2.0)  # show the outline for two seconds         self.canvas.delete(self.country_outline)         self.country_outline = None         self.next_country()      def next_country(self):         self.current_country = random.choice(COUNTRY_LIST)         self.label.config(text=f\"Click: {self.current_country}\")   if __name__ == \"__main__\":     root = tk.Tk()     app = AfricaQuizApp(root)     root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0418\u0433\u0440\u0430 \u00ab\u041f\u0438\u043d\u0431\u043e\u043b\u00bb<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442: <\/p>\n<blockquote>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u0438\u0433\u0440\u0443 \u0432 \u043f\u0438\u043d\u0431\u043e\u043b \u043d\u0430 Python \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c tkinter \u0438\u043b\u0438 turtle \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u0411\u0435\u0437 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 LLM \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u043b\u0438 \u043c\u043d\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043f\u0438\u043d\u0433-\u043f\u043e\u043d\u0433\u0430 \u0441 \u0448\u0430\u0440\u0438\u043a\u043e\u043c, \u0434\u0432\u0438\u0433\u0430\u044e\u0449\u0438\u043c\u0441\u044f \u0441 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e. \u041f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0443\u0442\u043e\u0447\u043d\u0435\u043d\u0438\u044f: \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0438\u043d\u0433-\u043f\u043e\u043d\u0433, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u0432\u0435 \u043b\u0430\u043f\u043a\u0438-\u0444\u043b\u0438\u043f\u043f\u0435\u0440\u044b, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0434\u043e\u043f\u043e\u0434\u043e\u0431\u043d\u0443\u044e \u0433\u0440\u0430\u0432\u0438\u0442\u0430\u0446\u0438\u044e \u0438 \u0432\u043e\u0440\u043e\u043d\u043a\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0435 \u0431\u043e\u0440\u0442\u0430, \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0435 \u0448\u0430\u0440 \u043a \u0444\u043b\u0438\u043f\u043f\u0435\u0440\u0430\u043c. LLM \u0442\u0430\u043a \u0438 \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0439 \u0438 \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u043b\u0438 \u043f\u043e\u043b\u0435 \u043f\u0438\u043d\u0431\u043e\u043b\u0430 \u043f\u0443\u0441\u0442\u044b\u043c. \u042f \u043d\u0435 \u043e\u0436\u0438\u0434\u0430\u043b \u00abSpace Cadet Pinball\u00bb, \u043d\u043e \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0438 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0439 \u043f\u0440\u043e\u043c\u043f\u0442 \u2014 \u043d\u0438\u0447\u0435\u0433\u043e \u043e\u0442\u0434\u0430\u043b\u0451\u043d\u043d\u043e \u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u043e\u0433\u043e \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u043e\u0441\u044c. \u042f \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0431\u043e\u0432\u0430\u043b \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043d\u0430 Pygame \u2014 \u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439, \u0435\u0441\u043b\u0438 tkinter \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0435\u0442.<\/p>\n<p>\u0412 \u043e\u0434\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043b\u0435\u0432\u044b\u0439 \u0444\u043b\u0438\u043f\u043f\u0435\u0440 \u0431\u044b\u043b \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u043c\u0435\u0449\u0451\u043d, \u043d\u043e \u0432\u0440\u0430\u0449\u0430\u043b\u0441\u044f \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443, \u0430 \u043f\u0440\u0430\u0432\u044b\u0439 \u2014 \u0441\u0442\u043e\u044f\u043b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u043d\u043e \u0432\u0440\u0430\u0449\u0430\u043b\u0441\u044f \u0432 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0443\u044e.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2e5\/4cc\/6e5\/2e54cc6e565fca283a2f3236641ee964.png\" width=\"438\" height=\"600\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/2e5\/4cc\/6e5\/2e54cc6e565fca283a2f3236641ee964.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2e5\/4cc\/6e5\/2e54cc6e565fca283a2f3236641ee964.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<details class=\"spoiler\">\n<summary>fail_pinball_1.py (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 Pygame)<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import pygame import sys import math  # Simple pinball game skeleton using Pygame def main():     pygame.init()     WIDTH, HEIGHT = 600, 800     screen = pygame.display.set_mode((WIDTH, HEIGHT))     clock = pygame.time.Clock()      # Ball properties     ball_pos = [WIDTH \/\/ 2, HEIGHT \/\/ 4]     ball_vel = [0, 0]     gravity = 0.5     radius = 10      # Flipper properties     left_flipper_angle = -30     right_flipper_angle = 30     flipper_length = 80     flipper_speed = 8      def draw_flipper(center, angle):         x1, y1 = center         x2 = x1 + flipper_length * math.cos(math.radians(angle))         y2 = y1 + flipper_length * math.sin(math.radians(angle))         pygame.draw.line(screen, (255, 255, 255), (x1, y1), (x2, y2), 8)      while True:         for event in pygame.event.get():             if event.type == pygame.QUIT:                 pygame.quit()                 sys.exit()          keys = pygame.key.get_pressed()         if keys[pygame.K_LEFT]:             left_flipper_angle = max(-60, left_flipper_angle - flipper_speed)         else:             left_flipper_angle = min(-30, left_flipper_angle + flipper_speed)         if keys[pygame.K_RIGHT]:             right_flipper_angle = min(60, right_flipper_angle + flipper_speed)         else:             right_flipper_angle = max(30, right_flipper_angle - flipper_speed)          # Update ball physics         ball_vel[1] += gravity         ball_pos[0] += ball_vel[0]         ball_pos[1] += ball_vel[1]          # Funnel walls         if ball_pos[1] &gt; HEIGHT - 200:             if ball_pos[0] &lt; WIDTH \/\/ 2:                 ball_vel[0] += 0.2             else:                 ball_vel[0] -= 0.2          # Boundary collision         if ball_pos[0] &lt; radius or ball_pos[0] &gt; WIDTH - radius:             ball_vel[0] *= -1         if ball_pos[1] &lt; radius:             ball_vel[1] *= -1          screen.fill((0, 0, 0))         # Draw funnel walls         pygame.draw.line(screen, (255, 255, 255), (0, HEIGHT - 200), (WIDTH\/\/2, HEIGHT), 4)         pygame.draw.line(screen, (255, 255, 255), (WIDTH, HEIGHT - 200), (WIDTH\/\/2, HEIGHT), 4)          # Draw flippers         draw_flipper((WIDTH\/\/2 - 100, HEIGHT - 100), left_flipper_angle)         draw_flipper((WIDTH\/\/2 + 100, HEIGHT - 100), 180-right_flipper_angle)          # Draw ball         pygame.draw.circle(screen, (255, 255, 255), (int(ball_pos[0]), int(ball_pos[1])), radius)          pygame.display.flip()         clock.tick(60)  if __name__ == \"__main__\":     main()<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0413\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u043a\u0440\u0443\u0433\u043e\u0432\u044b\u0445 \u043b\u0430\u0431\u0438\u0440\u0438\u043d\u0442\u043e\u0432<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442: <\/p>\n<blockquote>\n<p>\u041d\u0430\u043f\u0438\u0448\u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043d\u0430 Python, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e tkinter \u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043a\u0440\u0443\u0433\u043b\u043e\u0433\u043e \u043b\u0430\u0431\u0438\u0440\u0438\u043d\u0442\u0430. \u0421\u0442\u0435\u043d\u044b \u0438 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u043b\u0430\u0431\u0438\u0440\u0438\u043d\u0442\u0430 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u0440\u044f\u043c\u044b\u043c\u0438 \u043b\u0438\u043d\u0438\u044f\u043c\u0438 \u0438\u043b\u0438 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430\u043c\u0438. \u0418\u0433\u0440\u043e\u043a \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0432 \u0446\u0435\u043d\u0442\u0440\u0435 \u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0434\u043e \u0432\u044b\u0445\u043e\u0434\u0430 \u0441\u0432\u0435\u0440\u0445\u0443. \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0435\u043b\u043a\u0430\u043c\u0438 \u043d\u0430 \u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0435; \u0438\u0433\u0440\u043e\u043a \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0441\u043a\u0432\u043e\u0437\u044c \u0441\u0442\u0435\u043d\u044b.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: LLM \u0431\u0435\u0437 \u0442\u0440\u0443\u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0434\u043b\u044f \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u044b\u0445 \u043b\u0430\u0431\u0438\u0440\u0438\u043d\u0442\u043e\u0432. \u041d\u043e <a href=\"https:\/\/duckduckgo.com\/?t=ffab&amp;q=circular+maze&amp;ia=images&amp;iax=images\" rel=\"noopener noreferrer nofollow\">\u043a\u0440\u0443\u0433\u043e\u0432\u044b\u0435 \u043b\u0430\u0431\u0438\u0440\u0438\u043d\u0442\u044b<\/a> \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0438\u0441\u044c \u0436\u0430\u043b\u043a\u0438\u043c\u0438 \u043f\u043e\u0434\u043e\u0431\u0438\u044f\u043c\u0438: \u0441\u0442\u0435\u043d\u044b \u0440\u0430\u0441\u0441\u044b\u043f\u0430\u043d\u044b \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e, \u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0438\u0436\u0438\u043c\u044b\u043c\u0438 \u0443\u0447\u0430\u0441\u0442\u043a\u0430\u043c\u0438 \u0438 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c\u0438 \u043f\u043e\u0447\u0442\u0438 \u043f\u0440\u044f\u043c\u044b\u043c\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u043c\u0438. \u041f\u043e\u0440\u043e\u0439 LLM \u043f\u0438\u0441\u0430\u043b\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u0438 \u0432\u0441\u0435\u043c \u043e\u0431\u044a\u0451\u043c\u0435 \u043a\u043e\u0434\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u043b\u0430 \u043f\u0443\u0441\u0442\u043e\u0435 \u043e\u043a\u043d\u043e. \u0410 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441 \u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u044b \u0431\u044b\u043b\u043e \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u043b\u043e\u043c\u0430\u043d\u043e \u0438 \u043d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043c\u043e.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/367\/c1c\/0ca\/367c1c0cac46a15e05ecdf224bfbf8d8.png\" width=\"2338\" height=\"806\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/367\/c1c\/0ca\/367c1c0cac46a15e05ecdf224bfbf8d8.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/367\/c1c\/0ca\/367c1c0cac46a15e05ecdf224bfbf8d8.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<details class=\"spoiler\">\n<summary>fail_circlemaze_1.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import math import random  # --- Configuration --- CANVAS_SIZE = 800 MAZE_LEVELS = 15  # Number of concentric rings in the maze BACKGROUND_COLOR = '#F0F0F0' WALL_COLOR = '#333333' WALL_WIDTH = 2 SOLUTION_COLOR = '#E74C3C' # Color for the solution path (optional)  class CircularMaze:     \"\"\"     A class to generate and hold the data for a circular maze.     The maze is represented as a grid of cells, where each cell is a sector     of a concentric ring.     \"\"\"      def __init__(self, levels, sectors_in_first_level=8):         \"\"\"         Initializes the circular maze structure.          Args:             levels (int): The number of concentric rings (levels).             sectors_in_first_level (int): The number of cells in the innermost ring.                                           Each subsequent ring will have more sectors.         \"\"\"         self.levels = levels         self.grid = []         self.solution = {}          # --- FIX ---         # The original maze generation had a flaw where the number of sectors in         # one ring was not always an integer multiple of the sectors in the         # previous ring. This broke the neighbor-finding logic and resulted in         # unreachable areas.         #         # This corrected logic ensures that the ratio of sectors between         # adjacent rings is always an integer (1 or 2), allowing the DFS         # algorithm to create a perfect, fully connected maze. We now double         # the number of sectors every few rings.          sectors_list = []         current_sectors = sectors_in_first_level         # This value controls how often the maze complexity increases.         doubling_frequency = 3          for r in range(levels):             # Double the number of sectors every `doubling_frequency` rings.             if r &gt; 0 and r % doubling_frequency == 0:                 current_sectors *= 2             sectors_list.append(current_sectors)          # Create the grid using the pre-calculated sector counts.         for sectors in sectors_list:             # Each cell has two walls: 'cw' (clockwise) and 'out' (outward).             # A 'True' value means the wall exists.             self.grid.append([{'cw': True, 'out': True} for _ in range(sectors)])       def get_neighbors(self, r, c):         \"\"\"         Finds all valid neighbors for a given cell (r, c).         This handles the complexity of connecting cells between rings of         different sector counts.         \"\"\"         neighbors = []         sectors_curr = len(self.grid[r])                  # Clockwise neighbor         neighbors.append(((r, (c + 1) % sectors_curr), 'cw'))         # Counter-clockwise neighbor         neighbors.append(((r, (c - 1 + sectors_curr) % sectors_curr), 'ccw'))          # Outward neighbors         if r + 1 &lt; self.levels:             sectors_next = len(self.grid[r+1])             ratio = sectors_next \/ sectors_curr             for i in range(int(ratio)):                 neighbors.append(((r + 1, int(c * ratio) + i), 'out'))          # Inward neighbors         if r &gt; 0:             sectors_prev = len(self.grid[r-1])             ratio = sectors_curr \/ sectors_prev             neighbors.append(((r - 1, int(c \/ ratio)), 'in'))                      return neighbors      def generate(self):         \"\"\"         Generates the maze using a randomized depth-first search (DFS) algorithm.         It carves paths by removing walls between cells.         \"\"\"         start_r, start_c = random.randint(0, self.levels - 1), 0         stack = [(start_r, start_c)]         visited = set([(start_r, start_c)])          while stack:             current_r, current_c = stack[-1]                          # Find unvisited neighbors             unvisited_neighbors = []             for (nr, nc), direction in self.get_neighbors(current_r, current_c):                 if 0 &lt;= nr &lt; self.levels and 0 &lt;= nc &lt; len(self.grid[nr]):                     if (nr, nc) not in visited:                         unvisited_neighbors.append(((nr, nc), direction))              if unvisited_neighbors:                 # Choose a random unvisited neighbor                 (next_r, next_c), direction = random.choice(unvisited_neighbors)                                  # Remove the wall between the current cell and the chosen neighbor                 if direction == 'cw':                     self.grid[current_r][current_c]['cw'] = False                 elif direction == 'ccw':                     self.grid[next_r][next_c]['cw'] = False                 elif direction == 'out':                     self.grid[current_r][current_c]['out'] = False                 elif direction == 'in':                     self.grid[next_r][next_c]['out'] = False                  self.solution[(next_r, next_c)] = (current_r, current_c)                 visited.add((next_r, next_c))                 stack.append((next_r, next_c))             else:                 # Backtrack if there are no unvisited neighbors                 stack.pop()          # Create an entrance and an exit         self.grid[0][0]['cw'] = False # Entrance at the center         self.grid[self.levels-1][0]['out'] = False # Exit at the outer edge       def draw(self, canvas):         \"\"\"         Draws the entire maze on the provided tkinter canvas.         \"\"\"         canvas.delete(\"all\")         width = int(canvas.cget(\"width\"))         height = int(canvas.cget(\"height\"))         center_x, center_y = width \/ 2, height \/ 2                  # The first ring is a gap, so we start drawing from an offset         ring_thickness = (min(width, height) \/ 2) \/ (self.levels + 1)                  # Iterate through each cell to draw its walls         for r, level in enumerate(self.grid):             sectors = len(level)             angle_step = 360 \/ sectors                          for c, cell in enumerate(level):                 radius_inner = (r + 1) * ring_thickness                 radius_outer = (r + 2) * ring_thickness                                  angle_start = c * angle_step                 angle_end = (c + 1) * angle_step                                  # Draw the outward wall (an arc)                 if cell['out']:                     canvas.create_arc(                         center_x - radius_outer, center_y - radius_outer,                         center_x + radius_outer, center_y + radius_outer,                         start=angle_start, extent=angle_step,                         style=tk.ARC, outline=WALL_COLOR, width=WALL_WIDTH                     )                  # Draw the clockwise wall (a radial line)                 if cell['cw']:                     x1 = center_x + radius_inner * math.cos(math.radians(angle_end))                     y1 = center_y + radius_inner * math.sin(math.radians(angle_end))                     x2 = center_x + radius_outer * math.cos(math.radians(angle_end))                     y2 = center_y + radius_outer * math.sin(math.radians(angle_end))                     canvas.create_line(x1, y1, x2, y2, fill=WALL_COLOR, width=WALL_WIDTH)   def generate_and_draw_maze():     \"\"\"     Function to be called by the button to generate and draw a new maze.     \"\"\"     maze = CircularMaze(MAZE_LEVELS)     maze.generate()     maze.draw(canvas)  # --- Main Application Setup --- if __name__ == \"__main__\":     root = tk.Tk()     root.title(\"Circular Maze Generator\")     root.configure(bg=BACKGROUND_COLOR)      # --- UI Frame ---     ui_frame = tk.Frame(root, bg=BACKGROUND_COLOR)     ui_frame.pack(pady=10)      title_label = tk.Label(ui_frame, text=\"Circular Maze Generator\", font=(\"Helvetica\", 16), bg=BACKGROUND_COLOR)     title_label.pack(pady=(0, 10))      generate_button = tk.Button(         ui_frame,          text=\"Generate New Maze\",          font=(\"Helvetica\", 12),         command=generate_and_draw_maze     )     generate_button.pack()      # --- Canvas for Maze ---     canvas_frame = tk.Frame(root)     canvas_frame.pack(expand=True, fill=tk.BOTH, padx=20, pady=20)          canvas = tk.Canvas(         canvas_frame,          width=CANVAS_SIZE,          height=CANVAS_SIZE,          bg='white',          highlightthickness=0     )     canvas.pack(expand=True)      # Generate the first maze on startup     generate_and_draw_maze()      root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_circlemaze_2.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import math import random  # --- Configuration --- CANVAS_SIZE = 800 MAZE_LEVELS = 15  # Number of concentric rings in the maze BACKGROUND_COLOR = '#F0F0F0' WALL_COLOR = '#333333' WALL_WIDTH = 2 SOLUTION_COLOR = '#E74C3C' # Color for the solution path (optional)  class CircularMaze:     \"\"\"     A class to generate and hold the data for a circular maze.     The maze is represented as a grid of cells, where each cell is a sector     of a concentric ring.     \"\"\"      def __init__(self, levels, sectors_in_first_level=8):         \"\"\"         Initializes the circular maze structure.          Args:             levels (int): The number of concentric rings (levels).             sectors_in_first_level (int): The number of cells in the innermost ring.                                           Each subsequent ring will have more sectors.         \"\"\"         self.levels = levels         self.grid = []         self.solution = {}          # Create a grid where outer rings have more cells to keep cell sizes more uniform.         # The number of sectors in a ring is proportional to its radius.         for r in range(levels):             sectors = sectors_in_first_level * (r + 1)             # Each cell has two walls: 'cw' (clockwise) and 'out' (outward).             # A 'True' value means the wall exists.             self.grid.append([{'cw': True, 'out': True} for _ in range(sectors)])      def get_neighbors(self, r, c):         \"\"\"         Finds all valid neighbors for a given cell (r, c).         This handles the complexity of connecting cells between rings of         different sector counts.         \"\"\"         neighbors = []         sectors_curr = len(self.grid[r])                  # Clockwise neighbor         neighbors.append(((r, (c + 1) % sectors_curr), 'cw'))         # Counter-clockwise neighbor         neighbors.append(((r, (c - 1 + sectors_curr) % sectors_curr), 'ccw'))          # Outward neighbors         if r + 1 &lt; self.levels:             sectors_next = len(self.grid[r+1])             ratio = sectors_next \/ sectors_curr             for i in range(int(ratio)):                 neighbors.append(((r + 1, int(c * ratio) + i), 'out'))          # Inward neighbors         if r &gt; 0:             sectors_prev = len(self.grid[r-1])             ratio = sectors_curr \/ sectors_prev             neighbors.append(((r - 1, int(c \/ ratio)), 'in'))                      return neighbors      def generate(self):         \"\"\"         Generates the maze using a randomized depth-first search (DFS) algorithm.         It carves paths by removing walls between cells.         \"\"\"         start_r, start_c = random.randint(0, self.levels - 1), 0         stack = [(start_r, start_c)]         visited = set([(start_r, start_c)])          while stack:             current_r, current_c = stack[-1]                          # Find unvisited neighbors             unvisited_neighbors = []             for (nr, nc), direction in self.get_neighbors(current_r, current_c):                 if 0 &lt;= nr &lt; self.levels and 0 &lt;= nc &lt; len(self.grid[nr]):                     if (nr, nc) not in visited:                         unvisited_neighbors.append(((nr, nc), direction))              if unvisited_neighbors:                 # Choose a random unvisited neighbor                 (next_r, next_c), direction = random.choice(unvisited_neighbors)                                  # Remove the wall between the current cell and the chosen neighbor                 if direction == 'cw':                     self.grid[current_r][current_c]['cw'] = False                 elif direction == 'ccw':                     self.grid[next_r][next_c]['cw'] = False                 elif direction == 'out':                     self.grid[current_r][current_c]['out'] = False                 elif direction == 'in':                     self.grid[next_r][next_c]['out'] = False                  self.solution[(next_r, next_c)] = (current_r, current_c)                 visited.add((next_r, next_c))                 stack.append((next_r, next_c))             else:                 # Backtrack if there are no unvisited neighbors                 stack.pop()          # Create an entrance and an exit         self.grid[0][0]['cw'] = False # Entrance at the center         self.grid[self.levels-1][0]['out'] = False # Exit at the outer edge       def draw(self, canvas):         \"\"\"         Draws the entire maze on the provided tkinter canvas.         \"\"\"         canvas.delete(\"all\")         width = int(canvas.cget(\"width\"))         height = int(canvas.cget(\"height\"))         center_x, center_y = width \/ 2, height \/ 2                  # The first ring is a gap, so we start drawing from an offset         ring_thickness = (min(width, height) \/ 2) \/ (self.levels + 1)                  # Iterate through each cell to draw its walls         for r, level in enumerate(self.grid):             sectors = len(level)             angle_step = 360 \/ sectors                          for c, cell in enumerate(level):                 radius_inner = (r + 1) * ring_thickness                 radius_outer = (r + 2) * ring_thickness                                  angle_start = c * angle_step                 angle_end = (c + 1) * angle_step                                  # Draw the outward wall (an arc)                 if cell['out']:                     canvas.create_arc(                         center_x - radius_outer, center_y - radius_outer,                         center_x + radius_outer, center_y + radius_outer,                         start=angle_start, extent=angle_step,                         style=tk.ARC, outline=WALL_COLOR, width=WALL_WIDTH                     )                  # Draw the clockwise wall (a radial line)                 if cell['cw']:                     x1 = center_x + radius_inner * math.cos(math.radians(angle_end))                     y1 = center_y + radius_inner * math.sin(math.radians(angle_end))                     x2 = center_x + radius_outer * math.cos(math.radians(angle_end))                     y2 = center_y + radius_outer * math.sin(math.radians(angle_end))                     canvas.create_line(x1, y1, x2, y2, fill=WALL_COLOR, width=WALL_WIDTH)   def generate_and_draw_maze():     \"\"\"     Function to be called by the button to generate and draw a new maze.     \"\"\"     maze = CircularMaze(MAZE_LEVELS)     maze.generate()     maze.draw(canvas)  # --- Main Application Setup --- if __name__ == \"__main__\":     root = tk.Tk()     root.title(\"Circular Maze Generator\")     root.configure(bg=BACKGROUND_COLOR)      # --- UI Frame ---     ui_frame = tk.Frame(root, bg=BACKGROUND_COLOR)     ui_frame.pack(pady=10)      title_label = tk.Label(ui_frame, text=\"Circular Maze Generator\", font=(\"Helvetica\", 16), bg=BACKGROUND_COLOR)     title_label.pack(pady=(0, 10))      generate_button = tk.Button(         ui_frame,          text=\"Generate New Maze\",          font=(\"Helvetica\", 12),         command=generate_and_draw_maze     )     generate_button.pack()      # --- Canvas for Maze ---     canvas_frame = tk.Frame(root)     canvas_frame.pack(expand=True, fill=tk.BOTH, padx=20, pady=20)          canvas = tk.Canvas(         canvas_frame,          width=CANVAS_SIZE,          height=CANVAS_SIZE,          bg='white',          highlightthickness=0     )     canvas.pack(expand=True)      # Generate the first maze on startup     generate_and_draw_maze()      root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_circlemaze_3.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import math import random   # Dimensions of the polar maze. NUM_RINGS = 10           # Number of concentric circles (increasing this creates a deeper maze) NUM_SECTORS = 24         # Number of angular divisions (must be &gt;= 4 for a meaningful maze) CANVAS_SIZE = 600        # Width\/Height of the drawing canvas in pixels MARGIN = 20              # Padding around the circular maze  # Each cell is identified by (ring, sector) # We'll store whether each of its four \"walls\" exist: #   'in'  -&gt; wall between this ring and the ring below (toward center) #   'out' -&gt; wall between this ring and the ring above (toward outer edge) #   'left'  \/ 'right' -&gt; angular boundaries   def generate_polar_maze(rings, sectors):     # Initialize all walls as present and mark cells as unvisited.     maze = {}     for r in range(rings):         for s in range(sectors):             maze[(r, s)] = {                 \"visited\": False,                 \"in\": True,                 \"out\": True,                 \"left\": True,                 \"right\": True             }      def neighbors(cell):         r, s = cell         nbs = []         # inward neighbor         if r &gt; 0:             nbs.append(((r - 1), s, \"in\"))         # outward neighbor         if r &lt; rings - 1:             nbs.append(((r + 1), s, \"out\"))         # left\/clockwise neighbor         nbs.append((r, (s - 1) % sectors, \"left\"))         # right \/ counterclockwise neighbor         nbs.append((r, (s + 1) % sectors, \"right\"))         return nbs      # Carve maze using DFS     stack = [(0, 0)]     maze[(0, 0)][\"visited\"] = True     while stack:         cell = stack[-1]         unvisited = []         for nb_r, nb_s, direction in neighbors(cell):             if not maze[(nb_r, nb_s)][\"visited\"]:                 unvisited.append((nb_r, nb_s, direction))          if not unvisited:             stack.pop()         else:             nb_r, nb_s, direction = random.choice(unvisited)             # Knock down wall in the current cell and corresponding wall in neighbor             if direction == \"in\":                 maze[(cell[0], cell[1])][\"in\"] = False                 maze[(nb_r, nb_s)][\"out\"] = False             elif direction == \"out\":                 maze[(cell[0], cell[1])][\"out\"] = False                 maze[(nb_r, nb_s)][\"in\"] = False             elif direction == \"left\":                 maze[(cell[0], cell[1])][\"left\"] = False                 maze[(nb_r, nb_s)][\"right\"] = False             else:  # right                 maze[(cell[0], cell[1])][\"right\"] = False                 maze[(nb_r, nb_s)][\"left\"] = False              maze[(nb_r, nb_s)][\"visited\"] = True             stack.append((nb_r, nb_s))     return maze   def draw_polar_maze(canvas, maze, rings, sectors):     center = CANVAS_SIZE \/\/ 2     radius_step = (CANVAS_SIZE \/\/ 2 - MARGIN) \/ rings      for (r, s), walls in maze.items():         r_inner = r * radius_step         r_outer = (r + 1) * radius_step          start_angle = (360 \/ sectors) * s         end_angle = start_angle + (360 \/ sectors)          # Inward wall         if walls[\"in\"]:             canvas.create_arc(                 center - r_inner,                 center - r_inner,                 center + r_inner,                 center + r_inner,                 start=start_angle,                 extent=(360 \/ sectors),                 style=tk.ARC,                 width=2             )          # Outward wall         if walls[\"out\"]:             canvas.create_arc(                 center - r_outer,                 center - r_outer,                 center + r_outer,                 center + r_outer,                 start=start_angle,                 extent=(360 \/ sectors),                 style=tk.ARC,                 width=2             )          # Left (clockwise) wall         if walls[\"left\"]:             angle_rad = math.radians(start_angle)             x0 = center + r_inner * math.cos(angle_rad)             y0 = center - r_inner * math.sin(angle_rad)             x1 = center + r_outer * math.cos(angle_rad)             y1 = center - r_outer * math.sin(angle_rad)             canvas.create_line(x0, y0, x1, y1, width=2)          # Right (counterclockwise) wall         if walls[\"right\"]:             angle_rad = math.radians(end_angle)             x0 = center + r_inner * math.cos(angle_rad)             y0 = center - r_inner * math.sin(angle_rad)             x1 = center + r_outer * math.cos(angle_rad)             y1 = center - r_outer * math.sin(angle_rad)             canvas.create_line(x0, y0, x1, y1, width=2)   def main():     root = tk.Tk()     root.title(\"Circular Maze\")      canvas = tk.Canvas(root, width=CANVAS_SIZE, height=CANVAS_SIZE, bg=\"white\")     canvas.pack()      maze = generate_polar_maze(NUM_RINGS, NUM_SECTORS)     draw_polar_maze(canvas, maze, NUM_RINGS, NUM_SECTORS)      root.mainloop()   if __name__ == \"__main__\":     main()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_circlemaze_4.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import math import random  # --- Configuration Constants --- CANVAS_SIZE = 800 RINGS = 15  # Number of concentric rings in the maze WALL_COLOR = \"#1a237e\" PATH_COLOR = \"#e8eaf6\" PLAYER_COLOR = \"#ff5252\" WIN_COLOR = \"#4caf50\" WALL_THICKNESS = 2  class CircularMaze:     \"\"\"     This class handles the generation and drawing of the circular maze structure.     It uses a recursive backtracking algorithm adapted for a polar grid.     \"\"\"     def __init__(self, rings):         self.rings = rings         self.grid = []         self._setup_grid()         self._generate_maze()      def _setup_grid(self):         \"\"\"Initializes the grid data structure for the maze.\"\"\"         # The center is a single cell, ring 0         self.grid.append([{'N': True, 'E': True, 'S': True, 'W': True, 'visited': False}])          for r in range(1, self.rings + 1):             # The number of cells in a ring increases as it gets larger             num_cells = self._get_cells_in_ring(r)             ring_cells = [{'N': True, 'E': True, 'S': True, 'W': True, 'visited': False} for _ in range(num_cells)]             self.grid.append(ring_cells)      def _get_cells_in_ring(self, r):         \"\"\"Calculates the number of cells for a given ring.\"\"\"         # A simple formula to increase cells in outer rings         return int(r * 4 * 1.5) if r &gt; 0 else 1      def _generate_maze(self):         \"\"\"         Generates the maze paths using a randomized depth-first search         (recursive backtracking) algorithm.         \"\"\"         stack = [(0, 0)]  # Start at the center cell (ring 0, cell 0)         self.grid[0][0]['visited'] = True          while stack:             r, c = stack[-1]             neighbors = self._get_unvisited_neighbors(r, c)              if neighbors:                 nr, nc, direction = random.choice(neighbors)                  # Carve a path between the current cell and the neighbor                 self._remove_wall((r, c), (nr, nc), direction)                  self.grid[nr][nc]['visited'] = True                 stack.append((nr, nc))             else:                 # Backtrack                 stack.pop()          # Create an exit at the top of the outermost ring         exit_cell_index = len(self.grid[self.rings]) \/\/ 4         self.grid[self.rings][exit_cell_index]['N'] = False       def _get_unvisited_neighbors(self, r, c):         \"\"\"Finds all valid, unvisited neighbors for a given cell.\"\"\"         neighbors = []         num_cells_current = self._get_cells_in_ring(r)          # Clockwise neighbor (East)         east_c = (c + 1) % num_cells_current         if not self.grid[r][east_c]['visited']:             neighbors.append((r, east_c, 'E'))          # Counter-clockwise neighbor (West)         west_c = (c - 1 + num_cells_current) % num_cells_current         if not self.grid[r][west_c]['visited']:             neighbors.append((r, west_c, 'W'))          # Outward neighbor (South)         if r + 1 &lt;= self.rings:             num_cells_outer = self._get_cells_in_ring(r + 1)             # Find the corresponding cell in the outer ring             ratio = num_cells_outer \/ num_cells_current             for i in range(math.floor(ratio * c), math.floor(ratio * (c + 1))):                  if not self.grid[r + 1][i]['visited']:                     neighbors.append((r + 1, i, 'S'))          # Inward neighbor (North)         if r - 1 &gt;= 0:             num_cells_inner = self._get_cells_in_ring(r - 1)             # Find the corresponding cell in the inner ring             ratio = num_cells_current \/ num_cells_inner             north_c = int(c \/\/ ratio)             if not self.grid[r - 1][north_c]['visited']:                 neighbors.append((r - 1, north_c, 'N'))          return neighbors      def _remove_wall(self, cell1, cell2, direction):         \"\"\"Removes the wall between two adjacent cells.\"\"\"         r1, c1 = cell1         r2, c2 = cell2          if direction == 'E': # Moving Clockwise             self.grid[r1][c1]['E'] = False             self.grid[r2][c2]['W'] = False         elif direction == 'W': # Moving Counter-Clockwise             self.grid[r1][c1]['W'] = False             self.grid[r2][c2]['E'] = False         elif direction == 'S': # Moving Outward             self.grid[r1][c1]['S'] = False             self.grid[r2][c2]['N'] = False         elif direction == 'N': # Moving Inward             self.grid[r1][c1]['N'] = False             self.grid[r2][c2]['S'] = False      def draw(self, canvas):         \"\"\"Draws the entire maze on the tkinter canvas.\"\"\"         canvas.delete(\"all\")         canvas.create_rectangle(0, 0, CANVAS_SIZE, CANVAS_SIZE, fill=PATH_COLOR, outline=\"\")          center_x, center_y = CANVAS_SIZE \/ 2, CANVAS_SIZE \/ 2         ring_width = (CANVAS_SIZE \/ 2) \/ (self.rings + 1)          # Draw walls for each cell         for r in range(1, self.rings + 1):             num_cells = self._get_cells_in_ring(r)             radius_inner = r * ring_width             radius_outer = (r + 1) * ring_width              for c in range(num_cells):                 angle_start = (c \/ num_cells) * 360                 angle_extent = (1 \/ num_cells) * 360                  # Draw clockwise wall (radial line)                 if self.grid[r][c]['E']:                     x1 = center_x + radius_inner * math.cos(math.radians(angle_start + angle_extent))                     y1 = center_y + radius_inner * math.sin(math.radians(angle_start + angle_extent))                     x2 = center_x + radius_outer * math.cos(math.radians(angle_start + angle_extent))                     y2 = center_y + radius_outer * math.sin(math.radians(angle_start + angle_extent))                     canvas.create_line(x1, y1, x2, y2, fill=WALL_COLOR, width=WALL_THICKNESS)                  # Draw outward wall (arc)                 if self.grid[r][c]['S']:                     # Tkinter's arc bounding box is defined by top-left and bottom-right corners                     x0 = center_x - radius_outer                     y0 = center_y - radius_outer                     x1 = center_x + radius_outer                     y1 = center_y + radius_outer                     canvas.create_arc(x0, y0, x1, y1, start=angle_start, extent=angle_extent,                                       style=tk.ARC, outline=WALL_COLOR, width=WALL_THICKNESS)          # Draw the outermost boundary         final_radius = (self.rings + 1) * ring_width         x0 = center_x - final_radius         y0 = center_y - final_radius         x1 = center_x + final_radius         y1 = center_y + final_radius          exit_cell_index = len(self.grid[self.rings]) \/\/ 4         num_cells_outer = self._get_cells_in_ring(self.rings)         angle_start = (exit_cell_index \/ num_cells_outer) * 360         angle_extent = (1 \/ num_cells_outer) * 360          # Draw the boundary in two parts to leave an opening for the exit         canvas.create_arc(x0, y0, x1, y1, start=angle_start + angle_extent, extent=360-angle_extent,                           style=tk.ARC, outline=WALL_COLOR, width=WALL_THICKNESS * 2)   class Player:     \"\"\"Represents the player, handling movement, drawing, and win conditions.\"\"\"     def __init__(self, canvas, maze):         self.canvas = canvas         self.maze = maze         self.r = 0  # Start at ring 0         self.c = 0  # Start at cell 0         self.player_obj = None         self.has_won = False         self.draw()      def get_cell_center(self):         \"\"\"Calculates the pixel coordinates for the center of the player's current cell.\"\"\"         center_x, center_y = CANVAS_SIZE \/ 2, CANVAS_SIZE \/ 2         ring_width = (CANVAS_SIZE \/ 2) \/ (self.maze.rings + 1)          if self.r == 0:             return center_x, center_y          num_cells = self.maze._get_cells_in_ring(self.r)          # Angle to the middle of the cell         angle = ((self.c + 0.5) \/ num_cells) * 2 * math.pi          # Radius to the middle of the ring         radius = (self.r + 0.5) * ring_width          x = center_x + radius * math.cos(angle)         y = center_y + radius * math.sin(angle)         return x, y      def draw(self):         \"\"\"Draws or moves the player's icon on the canvas.\"\"\"         if self.player_obj:             self.canvas.delete(self.player_obj)          x, y = self.get_cell_center()         ring_width = (CANVAS_SIZE \/ 2) \/ (self.maze.rings + 1)         # Scale player size with the ring width         player_radius = ring_width \/ 4          self.player_obj = self.canvas.create_oval(             x - player_radius, y - player_radius,             x + player_radius, y + player_radius,             fill=PLAYER_COLOR, outline=\"\"         )      def move(self, event):         \"\"\"Handles key press events for player movement.\"\"\"         if self.has_won:             return          direction = event.keysym         current_cell = self.maze.grid[self.r][self.c]          moved = False         # --- Movement Logic ---         if direction == \"Up\": # Move Inward             if not current_cell['N']:                 num_cells_current = self.maze._get_cells_in_ring(self.r)                 num_cells_inner = self.maze._get_cells_in_ring(self.r - 1)                 ratio = num_cells_current \/ num_cells_inner                 self.r -= 1                 self.c = int(self.c \/\/ ratio)                 moved = True         elif direction == \"Down\": # Move Outward             if not current_cell['S']:                 num_cells_current = self.maze._get_cells_in_ring(self.r)                 num_cells_outer = self.maze._get_cells_in_ring(self.r + 1)                 ratio = num_cells_outer \/ num_cells_current                 # This is a simplification; find a cell that shares a border                 # For this generation, any cell within the ratio range is a valid path                 self.r += 1                 self.c = int(self.c * ratio) + random.randint(0, int(ratio-1))                 moved = True         elif direction == \"Left\": # Move Counter-Clockwise             if not current_cell['W']:                 num_cells = self.maze._get_cells_in_ring(self.r)                 self.c = (self.c - 1 + num_cells) % num_cells                 moved = True         elif direction == \"Right\": # Move Clockwise             if not current_cell['E']:                 num_cells = self.maze._get_cells_in_ring(self.r)                 self.c = (self.c + 1) % num_cells                 moved = True          if moved:             self.draw()             self.check_win()      def check_win(self):         \"\"\"Checks if the player has reached the exit and displays a win message.\"\"\"         exit_cell_index = len(self.maze.grid[self.maze.rings]) \/\/ 4         if self.r == self.maze.rings and self.c == exit_cell_index:             self.has_won = True             self.canvas.create_text(                 CANVAS_SIZE \/ 2, CANVAS_SIZE \/ 2,                 text=\"You Win!\",                 font=(\"Helvetica\", 60, \"bold\"),                 fill=WIN_COLOR             )  def main():     \"\"\"Main function to set up the game window and start the application.\"\"\"     root = tk.Tk()     root.title(\"Circular Maze\")      canvas = tk.Canvas(root, width=CANVAS_SIZE, height=CANVAS_SIZE, bg=PATH_COLOR)     canvas.pack()      # Generate and draw the maze     maze = CircularMaze(RINGS)     maze.draw(canvas)      # Create the player     player = Player(canvas, maze)      # Bind arrow keys to player movement     root.bind(\"&lt;KeyPress&gt;\", player.move)      # Center the window     root.update_idletasks()     width = root.winfo_width()     height = root.winfo_height()     x = (root.winfo_screenwidth() \/\/ 2) - (width \/\/ 2)     y = (root.winfo_screenheight() \/\/ 2) - (height \/\/ 2)     root.geometry('{}x{}+{}+{}'.format(width, height, x, y))      root.mainloop()  if __name__ == \"__main__\":     main()<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0418\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u0438\u0442\u0430\u0439\u0441\u043a\u0438\u0435 \u0441\u0447\u0451\u0442\u044b<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442:<\/p>\n<blockquote>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043d\u0430 Python, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e tkinter \u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u0441\u0447\u0451\u0442\u044b. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043a\u043b\u0438\u043a\u0430\u0442\u044c \u043f\u043e \u043a\u043e\u0441\u0442\u043e\u0447\u043a\u0430\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0438\u0445. \u0414\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0440\u044f\u0434\u044b \u201c\u043d\u0435\u0431\u0435\u0441\u043d\u044b\u0445\u201d \u0438 \u201c\u0437\u0435\u043c\u043d\u044b\u0445\u201d \u043a\u043e\u0441\u0442\u043e\u0447\u0435\u043a. \u0427\u0438\u0441\u043b\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u0447\u0451\u0442\u044b \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438, \u0434\u043e\u043b\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0432\u043d\u0438\u0437\u0443 \u043e\u043a\u043d\u0430.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u043e\u0442 LLM \u0440\u0438\u0441\u043e\u0432\u0430\u043b\u0438 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Abacus\" rel=\"noopener noreferrer nofollow\">\u0441\u0447\u0451\u0442\u044b<\/a>, \u043d\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0441\u0434\u0432\u0438\u0433\u0430 \u043a\u043e\u0441\u0442\u043e\u0447\u0435\u043a \u0432\u0441\u044f\u043a\u0438\u0439 \u0440\u0430\u0437 \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u043b\u043e\u0441\u044c \u0441\u043b\u043e\u043c\u0430\u043d\u043d\u044b\u043c. \u041f\u0440\u0438 \u043a\u043b\u0438\u043a\u0435 \u0441\u0434\u0432\u0438\u0433\u0430\u043b\u0438\u0441\u044c \u043d\u0435 \u0442\u0435 \u043a\u043e\u0441\u0442\u043e\u0447\u043a\u0438, \u0430 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043e\u043d\u0438 \u043d\u0435 \u043c\u043e\u0433\u043b\u0438. \u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0435 \u0447\u0438\u0441\u043b\u043e \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0440\u0430\u0441\u0445\u043e\u0434\u0438\u043b\u043e\u0441\u044c \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c \u0438 \u0438\u043d\u043e\u0433\u0434\u0430 \u0434\u0430\u0436\u0435 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u043e\u0441\u044c \u043e\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/199\/e65\/5b7\/199e655b78d2b41381c45a5a4c9fc987.png\" width=\"2968\" height=\"800\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/199\/e65\/5b7\/199e655b78d2b41381c45a5a4c9fc987.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/199\/e65\/5b7\/199e655b78d2b41381c45a5a4c9fc987.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<details class=\"spoiler\">\n<summary>fail_abacus_1.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk from tkinter import ttk import math  class Abacus:     def __init__(self, root):         self.root = root         self.root.title(\"Interactive Abacus\")         self.root.geometry(\"800x600\")         self.root.configure(bg='#8B4513')                  # Abacus configuration         self.num_columns = 10  # Number of digit columns         self.heaven_beads_per_column = 2  # Upper beads (worth 5 each)         self.earth_beads_per_column = 5   # Lower beads (worth 1 each)                  # Visual settings         self.bead_radius = 15         self.column_width = 70         self.heaven_section_height = 120         self.earth_section_height = 180         self.crossbar_height = 10                  # Data structure to track bead positions         # True = bead is \"active\" (counting), False = \"inactive\"         self.heaven_beads = [[False] * self.heaven_beads_per_column for _ in range(self.num_columns)]         self.earth_beads = [[False] * self.earth_beads_per_column for _ in range(self.num_columns)]                  self.setup_ui()         self.draw_abacus()         self.update_display()          def setup_ui(self):         # Main frame         self.main_frame = tk.Frame(self.root, bg='#8B4513')         self.main_frame.pack(expand=True, fill='both', padx=20, pady=20)                  # Canvas for abacus         canvas_width = self.num_columns * self.column_width + 40         canvas_height = self.heaven_section_height + self.earth_section_height + self.crossbar_height + 60                  self.canvas = tk.Canvas(             self.main_frame,             width=canvas_width,             height=canvas_height,             bg='#D2691E',             highlightthickness=2,             highlightbackground='#654321'         )         self.canvas.pack(pady=10)                  # Value display         self.value_label = tk.Label(             self.main_frame,             text=\"Value: 0\",             font=('Arial', 18, 'bold'),             bg='#8B4513',             fg='white'         )         self.value_label.pack(pady=10)                  # Reset button         self.reset_button = tk.Button(             self.main_frame,             text=\"Reset Abacus\",             command=self.reset_abacus,             font=('Arial', 12),             bg='#CD853F',             fg='black'         )         self.reset_button.pack(pady=5)                  # Instructions         instructions = tk.Label(             self.main_frame,             text=\"Click on beads to slide them. Heaven beads (top) = 5, Earth beads (bottom) = 1\",             font=('Arial', 10),             bg='#8B4513',             fg='lightgray',             wraplength=600         )         instructions.pack(pady=5)          def draw_abacus(self):         self.canvas.delete(\"all\")                  # Draw frame         frame_margin = 20         frame_width = self.num_columns * self.column_width         frame_height = self.heaven_section_height + self.earth_section_height + self.crossbar_height                  self.canvas.create_rectangle(             frame_margin, frame_margin,             frame_margin + frame_width, frame_margin + frame_height,             fill='#8B4513', outline='#654321', width=3         )                  # Draw crossbar (separates heaven and earth beads)         crossbar_y = frame_margin + self.heaven_section_height         self.canvas.create_rectangle(             frame_margin, crossbar_y,             frame_margin + frame_width, crossbar_y + self.crossbar_height,             fill='#654321', outline='#654321'         )                  # Draw vertical rods and beads for each column         for col in range(self.num_columns):             self.draw_column(col, frame_margin)                  # Draw column labels (place values)         for col in range(self.num_columns):             x = frame_margin + col * self.column_width + self.column_width \/\/ 2             place_value = 10 ** (self.num_columns - 1 - col)             if place_value &gt;= 1000000:                 label = f\"{place_value \/\/ 1000000}M\"             elif place_value &gt;= 1000:                 label = f\"{place_value \/\/ 1000}K\"             else:                 label = str(place_value)                          self.canvas.create_text(                 x, frame_margin - 10,                 text=label, font=('Arial', 8), fill='black'             )          def draw_column(self, col, frame_margin):         # Calculate column position         col_x = frame_margin + col * self.column_width + self.column_width \/\/ 2                  # Draw vertical rod         rod_top = frame_margin + 5         rod_bottom = frame_margin + self.heaven_section_height + self.earth_section_height + self.crossbar_height - 5         self.canvas.create_line(             col_x, rod_top, col_x, rod_bottom,             fill='#2F4F4F', width=4         )                  # Draw heaven beads (upper section)         heaven_start_y = frame_margin + 10         for bead_idx in range(self.heaven_beads_per_column):             if self.heaven_beads[col][bead_idx]:  # Active (counting)                 y = heaven_start_y + self.heaven_section_height - 40 - bead_idx * 35             else:  # Inactive                 y = heaven_start_y + bead_idx * 35                          bead_id = self.draw_bead(col_x, y, 'heaven', col, bead_idx)                  # Draw earth beads (lower section)         earth_start_y = frame_margin + self.heaven_section_height + self.crossbar_height + 10         for bead_idx in range(self.earth_beads_per_column):             if self.earth_beads[col][bead_idx]:  # Active (counting)                 y = earth_start_y + bead_idx * 32             else:  # Inactive                 y = earth_start_y + self.earth_section_height - 50 - bead_idx * 32                          bead_id = self.draw_bead(col_x, y, 'earth', col, bead_idx)          def draw_bead(self, x, y, bead_type, col, bead_idx):         # Choose colors based on type and state         if bead_type == 'heaven':             is_active = self.heaven_beads[col][bead_idx]             color = '#FFD700' if is_active else '#FFA500'  # Gold when active, orange when inactive         else:  # earth             is_active = self.earth_beads[col][bead_idx]             color = '#32CD32' if is_active else '#90EE90'  # Green when active, light green when inactive                  # Draw bead         bead_id = self.canvas.create_oval(             x - self.bead_radius, y - self.bead_radius,             x + self.bead_radius, y + self.bead_radius,             fill=color, outline='#654321', width=2,             tags=f\"{bead_type}_{col}_{bead_idx}\"         )                  # Bind click event         self.canvas.tag_bind(bead_id, \"&lt;Button-1&gt;\",                             lambda e, t=bead_type, c=col, b=bead_idx: self.toggle_bead(t, c, b))                  return bead_id          def toggle_bead(self, bead_type, col, bead_idx):         \"\"\"Toggle a bead between active and inactive states\"\"\"         if bead_type == 'heaven':             # For heaven beads, we need to maintain proper order             current_state = self.heaven_beads[col][bead_idx]             if current_state:  # Currently active, deactivate this and all above                 for i in range(bead_idx, self.heaven_beads_per_column):                     self.heaven_beads[col][i] = False             else:  # Currently inactive, activate this and all below                 for i in range(bead_idx + 1):                     self.heaven_beads[col][i] = True         else:  # earth beads             # For earth beads, maintain proper order             current_state = self.earth_beads[col][bead_idx]             if current_state:  # Currently active, deactivate this and all above                 for i in range(bead_idx, self.earth_beads_per_column):                     self.earth_beads[col][i] = False             else:  # Currently inactive, activate this and all below                 for i in range(bead_idx + 1):                     self.earth_beads[col][i] = True                  # Redraw and update display         self.draw_abacus()         self.update_display()          def calculate_value(self):         \"\"\"Calculate the total value represented by the abacus\"\"\"         total = 0                  for col in range(self.num_columns):             place_value = 10 ** (self.num_columns - 1 - col)                          # Count active heaven beads (worth 5 each)             heaven_count = sum(1 for bead in self.heaven_beads[col] if bead)                          # Count active earth beads (worth 1 each)             earth_count = sum(1 for bead in self.earth_beads[col] if bead)                          # Add to total             digit_value = (heaven_count * 5 + earth_count) * place_value             total += digit_value                  return total          def update_display(self):         \"\"\"Update the value display\"\"\"         value = self.calculate_value()         self.value_label.config(text=f\"Value: {value:,}\")          def reset_abacus(self):         \"\"\"Reset all beads to inactive state\"\"\"         for col in range(self.num_columns):             for bead_idx in range(self.heaven_beads_per_column):                 self.heaven_beads[col][bead_idx] = False             for bead_idx in range(self.earth_beads_per_column):                 self.earth_beads[col][bead_idx] = False                  self.draw_abacus()         self.update_display()  def main():     root = tk.Tk()     abacus = Abacus(root)     root.mainloop()  if __name__ == \"__main__\":     main()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_abacus_2.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk  # --- Constants --- # You can adjust these to change the abacus's appearance NUM_RODS = 13 BEAD_COLOR = \"#B22222\"  # Firebrick ROD_COLOR = \"#8B4513\"   # SaddleBrown FRAME_COLOR = \"#8B4513\" BAR_COLOR = \"#A0522D\"   # Sienna BG_COLOR = \"#F5DEB3\"    # Wheat BEAD_WIDTH = 50 BEAD_HEIGHT = 25 ROD_SPACING = 60 FRAME_THICKNESS = 20 BAR_THICKNESS = 10  # --- Main Application Class --- class AbacusApp:     \"\"\"     An interactive abacus application built with tkinter.     \"\"\"     def __init__(self, root):         \"\"\"Initializes the abacus application.\"\"\"         self.root = root         self.root.title(\"Interactive Abacus\")         self.root.resizable(False, False)          # Calculate canvas dimensions based on abacus size         self.canvas_width = (NUM_RODS + 1) * ROD_SPACING         self.canvas_height = 8 * BEAD_HEIGHT + 2 * FRAME_THICKNESS                  # --- Data Structure ---         # Stores the state of the abacus.         # For each rod, we store a list: [heaven_bead_active, num_earth_beads_active]         # heaven_bead_active: 1 if down (active), 0 if up (inactive)         # num_earth_beads_active: 0-4, number of earth beads pushed up (active)         self.bead_states = [[0, 0] for _ in range(NUM_RODS)]          # --- UI Elements ---         self.canvas = tk.Canvas(             root,             width=self.canvas_width,             height=self.canvas_height,             bg=BG_COLOR         )         self.canvas.pack(pady=10)          self.value_label = tk.Label(             root,             text=\"0\",             font=(\"Arial\", 24, \"bold\"),             pady=10         )         self.value_label.pack()          # --- Event Binding ---         self.canvas.bind(\"&lt;Button-1&gt;\", self.on_canvas_click)          # --- Initial Draw ---         self.draw_abacus()         self.update_value()      def draw_abacus(self):         \"\"\"Clears and redraws the entire abacus based on the current state.\"\"\"         self.canvas.delete(\"all\")         self.draw_frame()                  for i in range(NUM_RODS):             self.draw_rod(i)      def draw_frame(self):         \"\"\"Draws the outer frame and the central reckoning bar.\"\"\"         # Outer frame         self.canvas.create_rectangle(             FRAME_THICKNESS \/ 2, FRAME_THICKNESS \/ 2,             self.canvas_width - FRAME_THICKNESS \/ 2,             self.canvas_height - FRAME_THICKNESS \/ 2,             width=FRAME_THICKNESS,             outline=FRAME_COLOR         )         # Reckoning bar (the horizontal bar in the middle)         self.bar_y = self.canvas_height \/ 2 - 2.5 * BEAD_HEIGHT         self.canvas.create_rectangle(             FRAME_THICKNESS, self.bar_y - BAR_THICKNESS \/ 2,             self.canvas_width - FRAME_THICKNESS, self.bar_y + BAR_THICKNESS \/ 2,             fill=BAR_COLOR,             width=0         )      def draw_rod(self, rod_index):         \"\"\"Draws a single rod and its beads based on its state.\"\"\"         rod_x = (rod_index + 1) * ROD_SPACING                  # Draw the rod itself         self.canvas.create_line(             rod_x, FRAME_THICKNESS,             rod_x, self.canvas_height - FRAME_THICKNESS,             fill=ROD_COLOR,             width=2         )          # --- Draw Heaven Bead (value of 5) ---         heaven_state = self.bead_states[rod_index][0]         if heaven_state == 0: # Inactive (up)              y_center = self.bar_y - 1.5 * BEAD_HEIGHT         else: # Active (down)              y_center = self.bar_y - 0.5 * BEAD_HEIGHT         self.draw_bead(rod_x, y_center)          # --- Draw Earth Beads (value of 1 each) ---         earth_beads_up = self.bead_states[rod_index][1]                  # Draw active beads (up)         for i in range(earth_beads_up):             y_center = self.bar_y + (i + 0.5) * BEAD_HEIGHT             self.draw_bead(rod_x, y_center)                      # Draw inactive beads (down)         for i in range(4 - earth_beads_up):             y_center = self.bar_y + (earth_beads_up + i + 1.5) * BEAD_HEIGHT             self.draw_bead(rod_x, y_center)      def draw_bead(self, x, y):         \"\"\"Helper function to draw a single bead centered at (x, y).\"\"\"         self.canvas.create_oval(             x - BEAD_WIDTH \/ 2, y - BEAD_HEIGHT \/ 2,             x + BEAD_WIDTH \/ 2, y + BEAD_HEIGHT \/ 2,             fill=BEAD_COLOR,             width=2,             outline=\"black\"         )      def on_canvas_click(self, event):         \"\"\"Handles mouse clicks on the canvas to move beads.\"\"\"         # Determine which rod was clicked         rod_index = int((event.x - ROD_SPACING \/ 2) \/ ROD_SPACING)         if not (0 &lt;= rod_index &lt; NUM_RODS):             return # Click was outside the rods          # Check if the click was on the heaven bead (above the bar)         if event.y &lt; self.bar_y:             # Toggle heaven bead state             self.bead_states[rod_index][0] = 1 - self.bead_states[rod_index][0]         # Check if the click was on the earth beads (below the bar)         else:             # Determine which bead was clicked based on y-position             bead_pos = int((event.y - self.bar_y) \/ BEAD_HEIGHT)                          # Find the state of the clicked bead             num_up = self.bead_states[rod_index][1]                          if bead_pos &lt;= num_up: # Clicked on an 'up' bead                 # Move this bead and all above it down                 self.bead_states[rod_index][1] = bead_pos - 1             else: # Clicked on a 'down' bead                 # Move this bead and all below it up                 self.bead_states[rod_index][1] = bead_pos                  # Redraw and update the display         self.draw_abacus()         self.update_value()      def update_value(self):         \"\"\"Calculates and displays the number represented by the abacus.\"\"\"         total_value = 0         for i in range(NUM_RODS):             heaven_val = self.bead_states[i][0] * 5             earth_val = self.bead_states[i][1]             rod_value = heaven_val + earth_val                          # Calculate place value (units, tens, hundreds, etc. from right to left)             place_value = 10**(NUM_RODS - 1 - i)             total_value += rod_value * place_value                      self.value_label.config(text=f\"{total_value:,}\")   # --- Main Execution --- if __name__ == \"__main__\":     main_window = tk.Tk()     app = AbacusApp(main_window)     main_window.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_abacus_3.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk  # Constants for the abacus layout. NUM_COLUMNS = 6             # number of rods BEAD_RADIUS = 15 ROD_SPACING = 75 HEAVEN_Y = 50 EARTH_START_Y = 150 EARTH_SPACING = 40 HEAVEN_COUNT = 1 EARTH_COUNT = 4  class Bead:     def __init__(self, canvas, col, row, is_heaven):         self.canvas = canvas         self.col = col          # which rod this bead belongs to         self.row = row          # index within its group         self.is_heaven = is_heaven          x = (col + 1) * ROD_SPACING         if is_heaven:             y = HEAVEN_Y + row * 2 * BEAD_RADIUS         else:             y = EARTH_START_Y + row * 2 * BEAD_RADIUS          self.x = x         self.y = y         self.id = canvas.create_oval(             x - BEAD_RADIUS, y - BEAD_RADIUS,             x + BEAD_RADIUS, y + BEAD_RADIUS,             fill=\"tan\", outline=\"black\"         )         canvas.tag_bind(self.id, \"&lt;Button-1&gt;\", self.toggle)      def toggle(self, event):         \"\"\"         Slide a bead up or down depending on which group it belongs to.         \"\"\"         if self.is_heaven:             # Heaven bead can only move DOWN (towards the bar) or back UP.             target = HEAVEN_Y + BEAD_RADIUS*2             if self.y &lt; target:                 dy = (target - self.y)             else:                 dy = - (target - HEAVEN_Y)         else:             # Earth beads move upward toward the bar or back down to their start.             target = EARTH_START_Y - BEAD_RADIUS*2             if self.y &gt; target:                 dy = -(self.y - target)             else:                 dy = (EARTH_START_Y - self.y)          # Move only this bead         self.canvas.move(self.id, 0, dy)         self.y += dy         update_display()  def create_rods(canvas):     for c in range(NUM_COLUMNS):         x = (c + 1) * ROD_SPACING         canvas.create_line(x, HEAVEN_Y - 50, x, EARTH_START_Y + 200, width=4)  def get_value():     value = 0     for col in range(NUM_COLUMNS):         # Check heaven bead         heaven_bead = beads[(col, 0, True)]         if heaven_bead.y &gt; HEAVEN_Y + BEAD_RADIUS:             value += 5 * (10**(NUM_COLUMNS - 1 - col))          # Count earth beads that have moved up         earth_count = 0         for i in range(EARTH_COUNT):             b = beads[(col, i, False)]             if b.y &lt; EARTH_START_Y + i*2*BEAD_RADIUS:                 earth_count += 1         value += earth_count * (10**(NUM_COLUMNS - 1 - col))     return value  def update_display():     val = get_value()     label_var.set(f\"Value: {val}\")  root = tk.Tk() root.title(\"Abacus\")  canvas = tk.Canvas(root, width=(NUM_COLUMNS+1)*ROD_SPACING,                    height=350, bg=\"white\") canvas.pack()  # Draw the separating bar canvas.create_line(0, EARTH_START_Y - 30,                    (NUM_COLUMNS+1)*ROD_SPACING,                    EARTH_START_Y - 30, width=6)  create_rods(canvas)  # Create beads and store them in a dictionary beads = {} for col in range(NUM_COLUMNS):     # heaven beads     for r in range(HEAVEN_COUNT):         bead = Bead(canvas, col, r, True)         beads[(col, r, True)] = bead     # earth beads     for r in range(EARTH_COUNT):         bead = Bead(canvas, col, r, False)         beads[(col, r, False)] = bead  # Value display label_var = tk.StringVar() label_var.set(\"Value: 0\") label = tk.Label(root, textvariable=label_var, font=(\"Helvetica\", 14)) label.pack(pady=5)  root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u043a\u043e\u0434\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043c\u043a\u0430<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442:<\/p>\n<blockquote>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043d\u0430 Python, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e tkinter \u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443. \u042d\u0442\u043e \u0441\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u043a\u043e\u0434\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043c\u043a\u0430. \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u043c\u043e\u043a \u0441 \u0434\u0438\u0441\u043a\u043e\u043c \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0434\u0438\u0441\u043a, \u0434\u0432\u0438\u0433\u0430\u044f \u043c\u044b\u0448\u044c\u044e. \u041a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u0441\u0432\u0435\u0440\u0445\u0443. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0432\u0432\u0451\u043b \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044e, \u043f\u0440\u0438 \u043a\u043b\u0438\u043a\u0435 \u043f\u043e \u0437\u0430\u0449\u0451\u043b\u043a\u0435 \u043e\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u043e\u0442\u043a\u0440\u044b\u0442\u044c\u0441\u044f. \u041e\u043a\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043f\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0437\u0430\u043c\u043a\u0430.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u0425\u043e\u0442\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0440\u0438\u0441\u043e\u0432\u0430\u043b\u0438 \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u044b\u0439 \u043a\u043e\u0434\u043e\u0432\u044b\u0439 \u0437\u0430\u043c\u043e\u043a \u0438 \u0434\u0438\u0441\u043a \u0441 \u0446\u0438\u0444\u0440\u0430\u043c\u0438, \u0432\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0434\u0438\u0441\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u043a\u0430\u043a \u043f\u043e\u043f\u0430\u043b\u043e. \u0426\u0438\u0444\u0440\u044b \u043d\u0430 \u0434\u0438\u0441\u043a\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u043e\u0441\u0442\u0430\u0432\u0430\u043b\u0438\u0441\u044c \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043f\u0440\u0438 \u0432\u0440\u0430\u0449\u0435\u043d\u0438\u0438 (\u044f \u0438 \u043d\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430). \u0412\u0432\u0435\u0441\u0442\u0438 \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044e \u0431\u044b\u043b\u043e \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0438 \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u043c \u0432\u0432\u043e\u0434\u0435 \u043a\u043b\u0438\u043a \u043f\u043e \u0437\u0430\u0449\u0451\u043b\u043a\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u043b.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/804\/ea8\/69f\/804ea869f6878a6bbdd4a1d1c91f0e2b.png\" width=\"1296\" height=\"810\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/804\/ea8\/69f\/804ea869f6878a6bbdd4a1d1c91f0e2b.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/804\/ea8\/69f\/804ea869f6878a6bbdd4a1d1c91f0e2b.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<details class=\"spoiler\">\n<summary>fail_combo_1.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import math  # Configuration COMBINATION = (22, 7, 15)  # (clockwise, counter-clockwise, clockwise)  class CombinationLockApp:     def __init__(self, root):         self.root = root         self.root.title(\"Combination Lock\")          self.canvas = tk.Canvas(root, width=300, height=350, bg=\"white\")         self.canvas.pack()          # Variables         self.angle = 0.0         self.last_x = None         self.entered = []         self.turn_count = 0         self.is_unlocked = False          # Draw latch (simple rectangle)         self.latch = self.canvas.create_rectangle(120, 20, 180, 60, fill=\"gray\")         self.canvas.tag_bind(self.latch, \"&lt;Button-1&gt;\", self.try_open_latch)          # Draw dial *first* so that text is layered on top         self.dial_center = (150, 200)         self.dial = self.canvas.create_oval(50, 100, 250, 300, fill=\"lightgray\")          # Combination display         self.combo_text = self.canvas.create_text(             150,             90,             text=f\"Combination: {COMBINATION}\",             font=(\"Arial\", 12),             fill=\"black\"         )          # Draw tick marks and numbers         self.draw_ticks()          # Bind spin handlers         self.canvas.bind(\"&lt;ButtonPress-1&gt;\", self.start_spin)         self.canvas.bind(\"&lt;B1-Motion&gt;\", self.spin_dial)      def draw_ticks(self):         self.ticks = []         r = 100         for n in range(40):  # 0\u00e2\u20ac\u201c39             angle = math.radians(n * 9)             x = self.dial_center[0] + r * math.sin(angle)             y = self.dial_center[1] - r * math.cos(angle)             tick = self.canvas.create_text(                 x, y, text=str(n), font=(\"Arial\", 8), fill=\"black\"             )             self.ticks.append((tick, n))      def start_spin(self, event):         self.last_x = event.x      def spin_dial(self, event):         if self.is_unlocked:             return         delta = event.x - self.last_x         self.last_x = event.x          self.angle += delta * 0.8         self.update_dial_display(delta)      def update_dial_display(self, delta):         for tick, _ in self.ticks:             self.canvas.delete(tick)          self.ticks.clear()         r = 100         for n in range(40):             deg = n * 9 + self.angle             x = self.dial_center[0] + r * math.sin(math.radians(deg))             y = self.dial_center[1] - r * math.cos(math.radians(deg))             tick = self.canvas.create_text(                 x, y, text=str(n), font=(\"Arial\", 8), fill=\"black\"             )             self.ticks.append((tick, n))          current_number = int(((-self.angle) % 360) \/ 9) % 40          direction = -1 if delta &gt; 0 else 1         if not self.entered or self.entered[-1][1] != direction:             self.turn_count += 1             self.entered.append((current_number, direction))             if self.turn_count &gt; 3:                 self.entered = self.entered[-3:]                 self.turn_count = 3      def try_open_latch(self, event):         if self.is_unlocked:             return          numbers = [turn[0] for turn in self.entered[-3:]]         if tuple(numbers) == COMBINATION:             self.is_unlocked = True             self.canvas.itemconfig(self.latch, fill=\"green\")             self.canvas.itemconfig(self.combo_text, text=\"Unlocked!\", fill=\"green\")         else:             self.entered.clear()             self.turn_count = 0             self.canvas.itemconfig(self.combo_text, text=f\"Combination: {COMBINATION}\", fill=\"black\")   if __name__ == \"__main__\":     root = tk.Tk()     CombinationLockApp(root)     root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_combo_2.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import math  class CombinationLock:     def __init__(self, root):         self.root = root         self.root.title(\"Combination Lock Simulator\")         self.root.geometry(\"500x600\")         self.root.configure(bg='#2c3e50')                  # Lock settings         self.combination = [15, 35, 8]  # The correct combination         self.current_sequence = []         self.dial_angle = 0  # Current angle of the dial         self.is_dragging = False         self.last_mouse_angle = 0         self.latch_open = False                  # Colors         self.bg_color = '#2c3e50'         self.dial_color = '#34495e'         self.number_color = '#ecf0f1'         self.pointer_color = '#e74c3c'         self.latch_color = '#95a5a6'         self.open_latch_color = '#27ae60'                  self.setup_ui()              def setup_ui(self):         # Title and combination display         title_frame = tk.Frame(self.root, bg=self.bg_color)         title_frame.pack(pady=20)                  tk.Label(title_frame, text=\"Combination Lock\",                  font=('Arial', 20, 'bold'),                  fg=self.number_color, bg=self.bg_color).pack()                  tk.Label(title_frame, text=f\"Combination: {'-'.join(map(str, self.combination))}\",                  font=('Arial', 14),                  fg='#f39c12', bg=self.bg_color).pack(pady=5)                  # Current sequence display         self.sequence_label = tk.Label(title_frame, text=\"Entered: []\",                                       font=('Arial', 12),                                       fg=self.number_color, bg=self.bg_color)         self.sequence_label.pack(pady=5)                  # Instructions         instructions = tk.Label(title_frame,                                text=\"Drag the dial to enter numbers, then click the latch to open\",                                font=('Arial', 10),                                fg='#bdc3c7', bg=self.bg_color)         instructions.pack(pady=5)                  # Main canvas for the lock         self.canvas = tk.Canvas(self.root, width=400, height=400,                                 bg=self.bg_color, highlightthickness=0)         self.canvas.pack(pady=20)                  # Reset button         reset_btn = tk.Button(self.root, text=\"Reset\", command=self.reset_lock,                             font=('Arial', 12), bg='#e67e22', fg='white',                             activebackground='#d35400', relief='flat', pady=5)         reset_btn.pack(pady=10)                  # Bind mouse events         self.canvas.bind(\"&lt;Button-1&gt;\", self.on_mouse_down)         self.canvas.bind(\"&lt;B1-Motion&gt;\", self.on_mouse_drag)         self.canvas.bind(\"&lt;ButtonRelease-1&gt;\", self.on_mouse_up)                  self.draw_lock()              def draw_lock(self):         self.canvas.delete(\"all\")                  center_x, center_y = 200, 200         dial_radius = 120                  # Draw outer ring of lock         self.canvas.create_oval(center_x - dial_radius - 20, center_y - dial_radius - 20,                                center_x + dial_radius + 20, center_y + dial_radius + 20,                                fill='#34495e', outline='#2c3e50', width=3)                  # Draw main dial         self.canvas.create_oval(center_x - dial_radius, center_y - dial_radius,                                center_x + dial_radius, center_y + dial_radius,                                fill=self.dial_color, outline='#2c3e50', width=2)                  # Draw numbers around the dial         for i in range(40):  # 0-39 numbers             angle = math.radians(i * 9 - self.dial_angle)  # 9 degrees per number             number_radius = dial_radius - 25                          x = center_x + number_radius * math.cos(angle)             y = center_y + number_radius * math.sin(angle)                          # Highlight every 5th number             if i % 5 == 0:                 font_size = 12                 color = '#f39c12'             else:                 font_size = 10                 color = self.number_color                              self.canvas.create_text(x, y, text=str(i), fill=color,                                    font=('Arial', font_size, 'bold'))                  # Draw center hub         self.canvas.create_oval(center_x - 15, center_y - 15,                                center_x + 15, center_y + 15,                                fill='#2c3e50', outline='#34495e', width=2)                  # Draw pointer at top (fixed position)         pointer_y = center_y - dial_radius - 35         self.canvas.create_polygon(center_x, pointer_y,                                  center_x - 8, pointer_y + 15,                                  center_x + 8, pointer_y + 15,                                  fill=self.pointer_color, outline='#c0392b', width=2)                  # Draw latch         latch_color = self.open_latch_color if self.latch_open else self.latch_color         latch_y = center_y + dial_radius + 40                  if self.latch_open:             # Draw open latch (moved down)             self.canvas.create_rectangle(center_x - 20, latch_y + 10,                                        center_x + 20, latch_y + 30,                                        fill=latch_color, outline='#27ae60', width=2,                                        tags=\"latch\")             self.canvas.create_text(center_x, latch_y + 20, text=\"OPEN\",                                    fill='white', font=('Arial', 8, 'bold'))         else:             # Draw closed latch             self.canvas.create_rectangle(center_x - 20, latch_y,                                        center_x + 20, latch_y + 20,                                        fill=latch_color, outline='#7f8c8d', width=2,                                        tags=\"latch\")             self.canvas.create_text(center_x, latch_y + 10, text=\"PUSH\",                                    fill='white', font=('Arial', 8, 'bold'))          def get_mouse_angle(self, event):         center_x, center_y = 200, 200         dx = event.x - center_x         dy = event.y - center_y         return math.degrees(math.atan2(dy, dx))          def on_mouse_down(self, event):         # Check if clicking on latch         if self.canvas.find_closest(event.x, event.y)[0] in self.canvas.find_withtag(\"latch\"):             self.try_open_latch()             return                      # Check if clicking on dial area         center_x, center_y = 200, 200         distance = math.sqrt((event.x - center_x)**2 + (event.y - center_y)**2)                  if distance &lt;= 120:  # Within dial radius             self.is_dragging = True             self.last_mouse_angle = self.get_mouse_angle(event)          def on_mouse_drag(self, event):         if not self.is_dragging:             return                      current_angle = self.get_mouse_angle(event)         angle_diff = current_angle - self.last_mouse_angle                  # Handle angle wrap-around         if angle_diff &gt; 180:             angle_diff -= 360         elif angle_diff &lt; -180:             angle_diff += 360                      self.dial_angle += angle_diff         self.dial_angle = self.dial_angle % 360                  self.last_mouse_angle = current_angle         self.draw_lock()          def on_mouse_up(self, event):         if self.is_dragging:             self.is_dragging = False             # Record the number the pointer is pointing to             self.record_current_number()          def record_current_number(self):         # Calculate which number the pointer is currently pointing to         # The pointer is at the top, so we need to find which number is at angle 270 degrees (top)         pointer_angle = 270  # Top position         adjusted_angle = (pointer_angle + self.dial_angle) % 360                  # Each number is 9 degrees apart         number = int((adjusted_angle \/ 9) + 0.5) % 40                  self.current_sequence.append(number)                  # Keep only the last 3 numbers (length of combination)         if len(self.current_sequence) &gt; len(self.combination):             self.current_sequence = self.current_sequence[-len(self.combination):]                  # Update display         self.sequence_label.config(text=f\"Entered: {self.current_sequence}\")          def try_open_latch(self):         if len(self.current_sequence) == len(self.combination):             if self.current_sequence == self.combination:                 self.latch_open = True                 self.sequence_label.config(text=\"\u00f0\u0178\u017d\u2030 UNLOCKED! \u00f0\u0178\u017d\u2030\", fg='#27ae60')             else:                 self.sequence_label.config(text=\"\u00e2\u009d\u0152 Wrong combination!\", fg='#e74c3c')                 # Reset after wrong attempt                 self.root.after(1500, self.reset_sequence)         else:             self.sequence_label.config(text=\"Enter complete combination first!\", fg='#f39c12')                  self.draw_lock()          def reset_sequence(self):         self.current_sequence = []         self.sequence_label.config(text=\"Entered: []\", fg=self.number_color)          def reset_lock(self):         self.current_sequence = []         self.dial_angle = 0         self.latch_open = False         self.sequence_label.config(text=\"Entered: []\", fg=self.number_color)         self.draw_lock()  def main():     root = tk.Tk()     app = CombinationLock(root)     root.mainloop()  if __name__ == \"__main__\":     main()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_combo_3.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import math  # Configuration COMBINATION = (22, 7, 15)  # (clockwise, counter-clockwise, clockwise)  class CombinationLockApp:     def __init__(self, root):         self.root = root         self.root.title(\"Combination Lock\")          self.canvas = tk.Canvas(root, width=300, height=350, bg=\"white\")         self.canvas.pack()          # Variables         self.angle = 0.0         self.last_x = None         self.entered = []         self.turn_count = 0         self.is_unlocked = False          # Draw latch (simple rectangle)         self.latch = self.canvas.create_rectangle(120, 20, 180, 60, fill=\"gray\")         self.canvas.tag_bind(self.latch, \"&lt;Button-1&gt;\", self.try_open_latch)          # Combination display         self.combo_text = self.canvas.create_text(150, 90, text=f\"Combination: {COMBINATION}\",                                                   font=(\"Arial\", 12))          # Draw dial         self.dial_center = (150, 200)         self.dial = self.canvas.create_oval(50, 100, 250, 300, fill=\"lightgray\")         self.canvas.bind(\"&lt;ButtonPress-1&gt;\", self.start_spin)         self.canvas.bind(\"&lt;B1-Motion&gt;\", self.spin_dial)          # Draw tick marks and numbers         self.draw_ticks()      def draw_ticks(self):         self.ticks = []         r = 100         for n in range(40):  # standard 0-39 dial             angle = math.radians(n * 9)  # 360\/40 = 9 degrees per number             x = self.dial_center[0] + r*math.sin(angle)             y = self.dial_center[1] - r*math.cos(angle)             tick = self.canvas.create_text(x, y, text=str(n), font=(\"Arial\", 8))             self.ticks.append((tick, n))      def start_spin(self, event):         self.last_x = event.x      def spin_dial(self, event):         if self.is_unlocked:  # dial no longer spins             return         delta = event.x - self.last_x         self.last_x = event.x          # Rotate dial         self.angle += delta * 0.8  # speed factor         self.update_dial_display(delta)      def update_dial_display(self, delta):         # Move text numbers.         # First erase old         for tick, _ in self.ticks:             self.canvas.delete(tick)         # Recreate with new angle         self.ticks.clear()         r = 100         for n in range(40):             deg = n * 9 + self.angle             x = self.dial_center[0] + r * math.sin(math.radians(deg))             y = self.dial_center[1] - r * math.cos(math.radians(deg))             tick = self.canvas.create_text(x, y, text=str(n), font=(\"Arial\", 8))             self.ticks.append((tick, n))          # Track combination logic         current_number = int(((-self.angle) % 360) \/ 9) % 40          # Detect direction changes (left drag = clockwise, right drag = ccw)         direction = -1 if delta &gt; 0 else 1         if not self.entered or self.entered[-1][1] != direction:             self.turn_count += 1             self.entered.append((current_number, direction))             if self.turn_count &gt; 3:                 self.entered = self.entered[-3:]                 self.turn_count = 3      def try_open_latch(self, event):         if self.is_unlocked:             return          # Extract only the numbers from most recent 3 turns         numbers = [turn[0] for turn in self.entered[-3:]]         if tuple(numbers) == COMBINATION:             self.is_unlocked = True             self.canvas.itemconfig(self.latch, fill=\"green\")             self.canvas.itemconfig(self.combo_text, text=\"Unlocked!\")         else:             # Reset for another try             self.entered.clear()             self.turn_count = 0             self.canvas.itemconfig(self.combo_text, text=f\"Combination: {COMBINATION}\")  if __name__ == \"__main__\":     root = tk.Tk()     CombinationLockApp(root)     root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c \u0440\u043e\u0434\u043e\u0441\u043b\u043e\u0432\u043d\u043e\u0439<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442: <\/p>\n<blockquote>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043d\u0430 Python, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e tkinter \u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443. \u0421\u0434\u0435\u043b\u0430\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b \u0441\u0435\u043c\u044c\u0438. \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043b\u044e\u0434\u0435\u0439 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0430\u043c\u0438 \u0441 \u043f\u043e\u0434\u043f\u0438\u0441\u044c\u044e-\u0438\u043c\u0435\u043d\u0435\u043c. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0441 \u043e\u0434\u043d\u0438\u043c \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u043e\u043c. \u041f\u043e \u043a\u043b\u0438\u043a\u0443 \u043d\u0430 \u043a\u0432\u0430\u0434\u0440\u0430\u0442 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u044f, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0443\u043f\u0440\u0443\u0433\u0430, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0440\u0435\u0431\u0451\u043d\u043a\u0430, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0447\u0435\u043b\u043e\u0432\u0435\u043a\u0430. \u0414\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e \u043c\u0435\u0440\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043b\u044e\u0434\u0435\u0439. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u043b\u0438\u043d\u0438\u0438 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b \u0440\u043e\u0434\u043e\u0441\u043b\u043e\u0432\u043d\u043e\u0439.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u041f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043d\u0435\u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u041e\u043a\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u043b\u043e \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u0432\u0430\u0434\u0440\u0430\u0442 \u0441 \u0447\u0435\u043b\u043e\u0432\u0435\u043a\u043e\u043c, \u0438 \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u043c\u043d\u0435 \u0443\u0434\u0430\u0432\u0430\u043b\u043e\u0441\u044c \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u044f. \u041d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043b\u044e\u0431\u044b\u0445 \u0441\u0432\u044f\u0437\u0435\u0439 \u043b\u0438\u0431\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u043b\u043e, \u043b\u0438\u0431\u043e \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043b\u043e\u043c\u0430\u043b\u043e\/\u0441\u043a\u0440\u044b\u0432\u0430\u043b\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0443. \u041d\u0430\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0432\u0442\u043e\u0440\u043e\u0439 \u043a\u0432\u0430\u0434\u0440\u0430\u0442 \u0442\u0430\u043a \u0438 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u043d\u0438 \u0440\u0430\u0437\u0443.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dd7\/70c\/66f\/dd770c66f7be42f79c55d3c371044742.png\" width=\"831\" height=\"600\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/dd7\/70c\/66f\/dd770c66f7be42f79c55d3c371044742.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dd7\/70c\/66f\/dd770c66f7be42f79c55d3c371044742.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<details class=\"spoiler\">\n<summary>fail_familytree_1.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk from tkinter import simpledialog, messagebox  class Person:     def __init__(self, name=\"Person\"):         self.name = name         self.spouse = None         self.parents = []  # up to 2         self.children = []  class FamilyTreeApp(tk.Tk):     BOX_W = 80     BOX_H = 40     V_GAP = 50     H_GAP = 40      def __init__(self):         super().__init__()         self.title(\"Family Tree\")         self.canvas = tk.Canvas(self, width=1000, height=700, bg=\"white\")         self.canvas.pack(fill=\"both\", expand=True)          self.root_person = Person()         self.box_items = {}          self.canvas.bind(\"&lt;Button-1&gt;\", self.on_click)         self.draw_tree()      def edit_name(self, person):         new_name = simpledialog.askstring(\"Edit Name\", \"Enter name:\", initialvalue=person.name)         if new_name:             person.name = new_name             self.draw_tree()      def add_spouse(self, person):         if person.spouse:             messagebox.showinfo(\"Info\", \"This person already has a spouse.\")             return         person.spouse = Person(\"Spouse\")         person.spouse.spouse = person         self.draw_tree()      def add_child(self, person):         child = Person(\"Child\")         child.parents = [person]         if person.spouse:             child.parents.append(person.spouse)         for p in child.parents:             p.children.append(child)         self.draw_tree()      def add_parent(self, person):         if len(person.parents) &gt;= 2:             messagebox.showinfo(\"Info\", \"This person already has 2 parents.\")             return         parent = Person(\"Parent\")         parent.children.append(person)         person.parents.append(parent)         self.draw_tree()      def delete_person(self, person):         if person.spouse:             person.spouse.spouse = None         for p in person.parents:             if person in p.children:                 p.children.remove(person)         for ch in person.children:             if person in ch.parents:                 ch.parents.remove(person)         if person is self.root_person:             self.root_person = Person()         self.draw_tree()      def draw_tree(self):         self.canvas.delete(\"all\")         self.box_items.clear()         self.layout_positions = {}          # center the tree in the canvas         self.update_idletasks()         canvas_mid = self.canvas.winfo_width() \/\/ 2          self._assign_positions(self.root_person, 0, canvas_mid)         self._draw_connections(self.root_person)         self._draw_boxes(self.root_person)      def _assign_positions(self, person, level, offset):         if person in self.layout_positions:             return self.layout_positions[person][0]          if person.children:             child_xs = []             for ch in person.children:                 child_xs.append(self._assign_positions(ch, level + 1, offset))                 offset = child_xs[-1] + self.BOX_W + self.H_GAP             center_x = (child_xs[0] + child_xs[-1]) \/ 2         else:             center_x = offset             offset += self.BOX_W + self.H_GAP          self.layout_positions[person] = (center_x, level)         if person.spouse:             spouse_x = center_x + self.BOX_W + 10             self.layout_positions[person.spouse] = (spouse_x, level)         return center_x      def _draw_connections(self, person, drawn=set()):         if person.spouse and (person, person.spouse) not in drawn:             x, y = self._get_box_center(person)             sx, sy = self._get_box_center(person.spouse)             self.canvas.create_line(x, y, sx, sy)             drawn.add((person, person.spouse))             drawn.add((person.spouse, person))         for ch in person.children:             cx, cy = self._get_box_center(ch)             px, py = self._get_box_center(person)             self.canvas.create_line(px, py + self.BOX_H\/\/2, cx, cy - self.BOX_H\/\/2)             if person.spouse:                 sx, sy = self._get_box_center(person.spouse)                 self.canvas.create_line(sx, sy + self.BOX_H\/\/2, cx, cy - self.BOX_H\/\/2)             self._draw_connections(ch, drawn)      def _draw_boxes(self, person, drawn=set()):         if person in drawn:             return         drawn.add(person)         x, level = self.layout_positions[person]         y = level * (self.BOX_H + self.V_GAP)         box = self.canvas.create_rectangle(             x, y, x + self.BOX_W, y + self.BOX_H, outline=\"black\", fill=\"lightyellow\"         )         text = self.canvas.create_text(             x + self.BOX_W\/2, y + self.BOX_H\/2, text=person.name         )         self.box_items[box] = person         self.box_items[text] = person         if person.spouse:             self._draw_boxes(person.spouse, drawn)         for ch in person.children:             self._draw_boxes(ch, drawn)      def _get_box_center(self, person):         x, level = self.layout_positions[person]         y = level * (self.BOX_H + self.V_GAP)         return (x + self.BOX_W\/2, y + self.BOX_H\/2)      def on_click(self, event):         items = self.canvas.find_closest(event.x, event.y)         if not items:             return         person = self.box_items.get(items[0])         if person:             self.popup_menu(person, event.x_root, event.y_root)      def popup_menu(self, person, x, y):         menu = tk.Menu(self, tearoff=0)         menu.add_command(label=\"Edit name\", command=lambda: self.edit_name(person))         menu.add_command(label=\"Add spouse\", command=lambda: self.add_spouse(person))         menu.add_command(label=\"Add child\", command=lambda: self.add_child(person))         menu.add_command(label=\"Add parent\", command=lambda: self.add_parent(person))         menu.add_command(label=\"Delete\", command=lambda: self.delete_person(person))         menu.tk_popup(x, y)  if __name__ == \"__main__\":     app = FamilyTreeApp()     app.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_familytree_2.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk from tkinter import messagebox, simpledialog import math  class Person:     def __init__(self, name=\"New Person\", x=0, y=0):         self.name = name         self.x = x         self.y = y         self.spouse = None         self.children = []         self.parents = []         self.rect_id = None         self.text_id = None              def add_child(self, child):         if child not in self.children:             self.children.append(child)             child.parents.append(self)                  def add_parent(self, parent):         if parent not in self.parents:             self.parents.append(parent)             parent.children.append(self)                  def set_spouse(self, spouse):         if self.spouse:             self.spouse.spouse = None         self.spouse = spouse         if spouse:             spouse.spouse = self                  def remove_from_family(self):         # Remove spouse relationship         if self.spouse:             self.spouse.spouse = None                      # Remove from parents' children lists         for parent in self.parents:             if self in parent.children:                 parent.children.remove(self)                          # Remove from children's parents lists         for child in self.children:             if self in child.parents:                 child.parents.remove(self)  class FamilyTreeApp:     def __init__(self, root):         self.root = root         self.root.title(\"Family Tree Diagram\")         self.root.geometry(\"1000x700\")                  # Create canvas with scrollbars         self.create_canvas()                  # Data structures         self.people = []         self.selected_person = None                  # Layout parameters         self.person_width = 120         self.person_height = 60         self.level_height = 120         self.person_spacing = 150                  # Create initial person         initial_person = Person(\"Click to Edit\", 400, 300)         self.people.append(initial_person)                  self.draw_tree()              def create_canvas(self):         # Main frame         main_frame = tk.Frame(self.root)         main_frame.pack(fill=tk.BOTH, expand=True)                  # Canvas with scrollbars         self.canvas = tk.Canvas(main_frame, bg='white', scrollregion=(0, 0, 2000, 2000))                  v_scrollbar = tk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.canvas.yview)         h_scrollbar = tk.Scrollbar(main_frame, orient=tk.HORIZONTAL, command=self.canvas.xview)                  self.canvas.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set)                  v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)         h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)         self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)                  # Bind events         self.canvas.bind(\"&lt;Button-1&gt;\", self.on_click)         self.canvas.bind(\"&lt;Button-3&gt;\", self.on_right_click)  # Right click for context menu              def calculate_layout(self):         \"\"\"Calculate positions for all people in the tree\"\"\"         if not self.people:             return                      # Find root people (those with no parents)         roots = [p for p in self.people if not p.parents]         if not roots:             # If no roots found, pick the first person             roots = [self.people[0]]                      # Calculate levels         levels = {}         self.assign_levels(roots, levels, 0)                  # Group people by level         level_groups = {}         for person, level in levels.items():             if level not in level_groups:                 level_groups[level] = []             level_groups[level].append(person)                      # Calculate positions         canvas_width = 2000         for level, people_in_level in level_groups.items():             y = 100 + level * self.level_height                          # Sort people in level to keep families together             people_in_level.sort(key=lambda p: self.get_family_sort_key(p))                          total_width = len(people_in_level) * self.person_spacing             start_x = (canvas_width - total_width) \/\/ 2                          for i, person in enumerate(people_in_level):                 person.x = start_x + i * self.person_spacing                 person.y = y                      def assign_levels(self, people, levels, level):         \"\"\"Recursively assign levels to people\"\"\"         for person in people:             if person not in levels:                 levels[person] = level                 # Process children at next level                 if person.children:                     self.assign_levels(person.children, levels, level + 1)                          def get_family_sort_key(self, person):         \"\"\"Generate a sort key to keep families together\"\"\"         # Use parent names as primary sort key         if person.parents:             return ''.join(sorted([p.name for p in person.parents]))         return person.name              def draw_tree(self):         \"\"\"Draw the entire family tree\"\"\"         self.canvas.delete(\"all\")                  # Calculate layout         self.calculate_layout()                  # Draw connections first (so they appear behind rectangles)         self.draw_connections()                  # Draw people         for person in self.people:             self.draw_person(person)                  def draw_person(self, person):         \"\"\"Draw a single person as a rectangle with text\"\"\"         x1 = person.x - self.person_width \/\/ 2         y1 = person.y - self.person_height \/\/ 2         x2 = person.x + self.person_width \/\/ 2         y2 = person.y + self.person_height \/\/ 2                  # Choose color based on selection         color = \"lightblue\" if person == self.selected_person else \"lightgray\"                  # Draw rectangle         person.rect_id = self.canvas.create_rectangle(             x1, y1, x2, y2,              fill=color, outline=\"black\", width=2,             tags=f\"person_{id(person)}\"         )                  # Draw text         person.text_id = self.canvas.create_text(             person.x, person.y,              text=person.name,              font=(\"Arial\", 10, \"bold\"),             width=self.person_width - 10,             tags=f\"person_{id(person)}\"         )              def draw_connections(self):         \"\"\"Draw family relationship lines\"\"\"         for person in self.people:             # Draw spouse connection             if person.spouse and id(person) &lt; id(person.spouse):  # Draw only once per couple                 self.draw_spouse_line(person, person.spouse)                              # Draw parent-child connections             if person.children:                 self.draw_parent_child_lines(person)                      def draw_spouse_line(self, person1, person2):         \"\"\"Draw line between spouses\"\"\"         self.canvas.create_line(             person1.x, person1.y,             person2.x, person2.y,             fill=\"red\", width=3, tags=\"connection\"         )              def draw_parent_child_lines(self, parent):         \"\"\"Draw lines from parent to children\"\"\"         if not parent.children:             return                      # Find the spouse to determine the family unit center         spouse = parent.spouse         if spouse:             family_x = (parent.x + spouse.x) \/\/ 2             family_y = min(parent.y, spouse.y)         else:             family_x = parent.x             family_y = parent.y                      # Draw vertical line down from family unit         children_y = min([child.y for child in parent.children])         mid_y = family_y + (children_y - family_y) \/\/ 2                  self.canvas.create_line(             family_x, family_y + self.person_height \/\/ 2,             family_x, mid_y,             fill=\"blue\", width=2, tags=\"connection\"         )                  # Draw horizontal line across children         if len(parent.children) &gt; 1:             left_x = min([child.x for child in parent.children])             right_x = max([child.x for child in parent.children])                          self.canvas.create_line(                 left_x, mid_y,                 right_x, mid_y,                 fill=\"blue\", width=2, tags=\"connection\"             )                      # Draw vertical lines to each child         for child in parent.children:             self.canvas.create_line(                 child.x, mid_y,                 child.x, child.y - self.person_height \/\/ 2,                 fill=\"blue\", width=2, tags=\"connection\"             )                  def on_click(self, event):         \"\"\"Handle left mouse click\"\"\"         clicked_item = self.canvas.find_closest(event.x, event.y)[0]                  # Find which person was clicked         clicked_person = None         for person in self.people:             if (person.rect_id == clicked_item or person.text_id == clicked_item):                 clicked_person = person                 break                          if clicked_person:             self.selected_person = clicked_person             self.show_edit_menu(clicked_person, event.x, event.y)         else:             self.selected_person = None                      self.draw_tree()              def on_right_click(self, event):         \"\"\"Handle right mouse click - same as left click for simplicity\"\"\"         self.on_click(event)              def show_edit_menu(self, person, x, y):         \"\"\"Show context menu for editing person\"\"\"         menu = tk.Menu(self.root, tearoff=0)         menu.add_command(label=\"Edit Name\", command=lambda: self.edit_name(person))         menu.add_command(label=\"Add Spouse\", command=lambda: self.add_spouse(person))         menu.add_command(label=\"Add Child\", command=lambda: self.add_child(person))         menu.add_command(label=\"Add Parent\", command=lambda: self.add_parent(person))         menu.add_separator()         menu.add_command(label=\"Delete Person\", command=lambda: self.delete_person(person))                  try:             menu.tk_popup(event.x_root, event.y_root)         finally:             menu.grab_release()                  def edit_name(self, person):         \"\"\"Edit person's name\"\"\"         new_name = simpledialog.askstring(\"Edit Name\", \"Enter new name:\", initialvalue=person.name)         if new_name:             person.name = new_name             self.draw_tree()                  def add_spouse(self, person):         \"\"\"Add spouse to person\"\"\"         if person.spouse:             messagebox.showwarning(\"Warning\", f\"{person.name} already has a spouse!\")             return                      spouse_name = simpledialog.askstring(\"Add Spouse\", \"Enter spouse's name:\")         if spouse_name:             spouse = Person(spouse_name, person.x + self.person_spacing, person.y)             person.set_spouse(spouse)             self.people.append(spouse)             self.draw_tree()                  def add_child(self, person):         \"\"\"Add child to person\"\"\"         child_name = simpledialog.askstring(\"Add Child\", \"Enter child's name:\")         if child_name:             child = Person(child_name, person.x, person.y + self.level_height)             person.add_child(child)                          # If person has spouse, add child to spouse too             if person.spouse:                 person.spouse.add_child(child)                              self.people.append(child)             self.draw_tree()                  def add_parent(self, person):         \"\"\"Add parent to person\"\"\"         parent_name = simpledialog.askstring(\"Add Parent\", \"Enter parent's name:\")         if parent_name:             parent = Person(parent_name, person.x, person.y - self.level_height)             person.add_parent(parent)             self.people.append(parent)             self.draw_tree()                  def delete_person(self, person):         \"\"\"Delete person from tree\"\"\"         if len(self.people) == 1:             messagebox.showwarning(\"Warning\", \"Cannot delete the last person!\")             return                      result = messagebox.askyesno(\"Confirm Delete\",                                     f\"Are you sure you want to delete {person.name}?\")         if result:             person.remove_from_family()             self.people.remove(person)             if self.selected_person == person:                 self.selected_person = None             self.draw_tree()  def main():     root = tk.Tk()     app = FamilyTreeApp(root)     root.mainloop()  if __name__ == \"__main__\":     main()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_familytree_3.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk from tkinter import simpledialog, messagebox  class Person:     def __init__(self, name=\"Person\"):         self.name = name         self.spouse = None         self.parents = []  # up to 2         self.children = []  class FamilyTreeApp(tk.Tk):     BOX_W = 80     BOX_H = 40     V_GAP = 50     H_GAP = 40      def __init__(self):         super().__init__()         self.title(\"Family Tree\")         self.canvas = tk.Canvas(self, width=1000, height=700, bg=\"white\")         self.canvas.pack(fill=\"both\", expand=True)          self.root_person = Person()         self.box_items = {}  # maps canvas item id -&gt; Person          self.canvas.bind(\"&lt;Button-1&gt;\", self.on_click)         self.draw_tree()      # ----- Tree editing actions -----     def edit_name(self, person):         new_name = simpledialog.askstring(\"Edit Name\", \"Enter name:\", initialvalue=person.name)         if new_name:             person.name = new_name             self.draw_tree()      def add_spouse(self, person):         if person.spouse:             messagebox.showinfo(\"Info\", \"This person already has a spouse.\")             return         person.spouse = Person(\"Spouse\")         person.spouse.spouse = person         self.draw_tree()      def add_child(self, person):         child = Person(\"Child\")         # Set parents         child.parents = [person]         if person.spouse:  # If married, both become parents             child.parents.append(person.spouse)         # Add child to each parent         for p in child.parents:             p.children.append(child)         self.draw_tree()      def add_parent(self, person):         if len(person.parents) &gt;= 2:             messagebox.showinfo(\"Info\", \"This person already has 2 parents.\")             return         parent = Person(\"Parent\")         parent.children.append(person)         person.parents.append(parent)         self.draw_tree()      def delete_person(self, person):         # remove from spouse         if person.spouse:             person.spouse.spouse = None         # remove from parents' children list         for p in person.parents:             if person in p.children:                 p.children.remove(person)         # remove from children's parents         for ch in person.children:             if person in ch.parents:                 ch.parents.remove(person)         # If deleting the root person, reset to a new single person         if person is self.root_person:             self.root_person = Person()         self.draw_tree()      # ----- Canvas \/ Drawing -----     def draw_tree(self):         self.canvas.delete(\"all\")         self.box_items.clear()         self.layout_positions = {}         self._assign_positions(self.root_person, 0, 0)         self._draw_connections(self.root_person)         self._draw_boxes(self.root_person)      def _assign_positions(self, person, level, offset):         # Recursive layout: compute x positions of children then center parents         if person in self.layout_positions:             return self.layout_positions[person][0]          if person.children:             child_xs = []             for ch in person.children:                 child_xs.append(self._assign_positions(ch, level + 1, offset))                 offset = child_xs[-1] + self.BOX_W + self.H_GAP             center_x = (child_xs[0] + child_xs[-1]) \/ 2         else:             center_x = offset             offset += self.BOX_W + self.H_GAP          self.layout_positions[person] = (center_x, level)         # Spouse should have same level and adjacent x         if person.spouse:             spouse_x = center_x + self.BOX_W + 10             self.layout_positions[person.spouse] = (spouse_x, level)         return center_x      def _draw_connections(self, person, drawn=set()):         # Draw spouse connection         if person.spouse and (person, person.spouse) not in drawn:             x, y = self._get_box_center(person)             sx, sy = self._get_box_center(person.spouse)             self.canvas.create_line(x, y, sx, sy)             drawn.add((person, person.spouse))             drawn.add((person.spouse, person))         # Draw children connections         for ch in person.children:             cx, cy = self._get_box_center(ch)             px, py = self._get_box_center(person)             self.canvas.create_line(px, py + self.BOX_H\/\/2, cx, cy - self.BOX_H\/\/2)             # If spouse exists, connect spouse too             if person.spouse:                 sx, sy = self._get_box_center(person.spouse)                 self.canvas.create_line(sx, sy + self.BOX_H\/\/2, cx, cy - self.BOX_H\/\/2)             self._draw_connections(ch, drawn)      def _draw_boxes(self, person, drawn=set()):         if person in drawn:             return         drawn.add(person)         x, level = self.layout_positions[person]         y = level * (self.BOX_H + self.V_GAP)         box = self.canvas.create_rectangle(             x, y, x + self.BOX_W, y + self.BOX_H, outline=\"black\", fill=\"lightyellow\"         )         text = self.canvas.create_text(             x + self.BOX_W\/2, y + self.BOX_H\/2, text=person.name         )         self.box_items[box] = person         self.box_items[text] = person         if person.spouse:             self._draw_boxes(person.spouse, drawn)         for ch in person.children:             self._draw_boxes(ch, drawn)      def _get_box_center(self, person):         x, level = self.layout_positions[person]         y = level * (self.BOX_H + self.V_GAP)         return (x + self.BOX_W\/2, y + self.BOX_H\/2)      # ----- Mouse click handler -----     def on_click(self, event):         item = self.canvas.find_closest(event.x, event.y)         person = self.box_items.get(item[0])         if person:             self.popup_menu(person, event.x_root, event.y_root)      def popup_menu(self, person, x, y):         menu = tk.Menu(self, tearoff=0)         menu.add_command(label=\"Edit name\", command=lambda: self.edit_name(person))         menu.add_command(label=\"Add spouse\", command=lambda: self.add_spouse(person))         menu.add_command(label=\"Add child\", command=lambda: self.add_child(person))         menu.add_command(label=\"Add parent\", command=lambda: self.add_parent(person))         menu.add_command(label=\"Delete\", command=lambda: self.delete_person(person))         menu.tk_popup(x, y)  if __name__ == \"__main__\":     app = FamilyTreeApp()     app.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u043b\u0430\u0432\u043e\u0432\u043e\u0439 \u043b\u0430\u043c\u043f\u044b<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442: <\/p>\n<blockquote>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043d\u0430 Python, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e tkinter \u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443. \u042d\u0442\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0441\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u043b\u0430\u0432\u043e\u0432\u043e\u0439 \u043b\u0430\u043c\u043f\u044b, \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u043a\u0430\u043f\u043b\u0438, \u043c\u044f\u0433\u043a\u043e \u043f\u043b\u0430\u0432\u0430\u044e\u0449\u0438\u0435 \u043f\u043e \u043e\u043a\u043d\u0443. \u041d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439 \u043a\u0440\u0443\u0433\u0438, \u044d\u043b\u043b\u0438\u043f\u0441\u044b \u0438\u043b\u0438 \u043c\u043d\u043e\u0433\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0438 \u0434\u043b\u044f \u0444\u043e\u0440\u043c\u044b \u043a\u0430\u043f\u0435\u043b\u044c. \u0414\u043b\u044f \u043a\u043e\u043d\u0442\u0443\u0440\u043e\u0432 \u043a\u0430\u043f\u0435\u043b\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439 \u043a\u0440\u0438\u0432\u044b\u0435 \u0411\u0435\u0437\u044c\u0435. \u041a\u0430\u043f\u043b\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u0441\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f, \u043a\u0430\u043a \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0439 \u043b\u0430\u0432\u043e\u0432\u043e\u0439 \u043b\u0430\u043c\u043f\u0435.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u041d\u0430 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Lava_lamp\" rel=\"noopener noreferrer nofollow\">\u043b\u0430\u0432\u043e\u0432\u0443\u044e \u043b\u0430\u043c\u043f\u0443<\/a> \u044d\u0442\u043e \u043d\u0435 \u0431\u044b\u043b\u043e \u043f\u043e\u0445\u043e\u0436\u0435. \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u043b\u0438 \u0434\u0432\u0438\u0436\u0443\u0449\u0438\u0435\u0441\u044f \u0444\u0438\u0433\u0443\u0440\u044b \u2014 \u0438 \u043d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451. \u041f\u0440\u0438 \u0441\u0431\u043b\u0438\u0436\u0435\u043d\u0438\u0438 \u043e\u043d\u0438 \u0433\u0440\u0443\u0431\u043e \u00ab\u0441\u043b\u0438\u0432\u0430\u043b\u0438\u0441\u044c\u00bb: \u043e\u0434\u043d\u0430 \u043a\u0430\u043f\u043b\u044f \u0438\u0441\u0447\u0435\u0437\u0430\u043b\u0430, \u0430 \u0434\u0440\u0443\u0433\u0430\u044f \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u043b\u0430\u0441\u044c \u0432 \u0440\u0430\u0437\u043c\u0435\u0440\u0435. \u0420\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u043b\u043e; \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u043c\u0435\u043b\u043a\u0438\u0435 \u043a\u0430\u043f\u043b\u0438 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u043b\u0438 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0438\u0437 \u043d\u0438\u043e\u0442\u043a\u0443\u0434\u0430. \u041a\u0430\u043f\u043b\u0438, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0434\u0440\u043e\u0436\u0430\u043b\u0438, \u0431\u0443\u0434\u0442\u043e \u043d\u0435\u0440\u0432\u043d\u044b\u0435 \u0447\u0438\u0445\u0443\u0430\u0445\u0443\u0430.<\/p>\n<p>\u041e\u0434\u043d\u0430 \u0438\u0437 LLM \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0438\u0441\u043e\u0432\u0430\u043b\u0430 \u043a\u043e\u043d\u0442\u0443\u0440\u044b \u043a\u0430\u043f\u0435\u043b\u044c.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8d7\/047\/892\/8d70478925af3b0cd7d31567e719e6db.png\" width=\"1036\" height=\"810\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/8d7\/047\/892\/8d70478925af3b0cd7d31567e719e6db.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8d7\/047\/892\/8d70478925af3b0cd7d31567e719e6db.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<details class=\"spoiler\">\n<summary>fail_lavalamp_1.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import random import math  WINDOW_WIDTH = 400 WINDOW_HEIGHT = 600 WAX_COLOR = \"#f84d6a\" BACKGROUND_COLOR = \"#101030\"  NUM_SIDES = 12        # number of vertices per blob WIGGLE_AMOUNT = 4     # how much each point can change per frame  class WaxBlob:     def __init__(self, canvas):         self.radius = random.randint(30, 60)         self.x = random.randint(self.radius, WINDOW_WIDTH - self.radius)         self.y = random.randint(self.radius, WINDOW_HEIGHT - self.radius)          self.vy = random.uniform(-0.8, 0.8)         self.canvas = canvas          # generate random radial offsets so shape isn't a perfect circle         self.offsets = [random.uniform(0.8, 1.2) for _ in range(NUM_SIDES)]         self.id = canvas.create_polygon(self._get_points(),                                         fill=WAX_COLOR, outline=\"\")      def _get_points(self):         points = []         for i in range(NUM_SIDES):             angle = (2 * math.pi \/ NUM_SIDES) * i             r = self.radius * self.offsets[i]             px = self.x + r * math.cos(angle)             py = self.y + r * math.sin(angle)             points.extend([px, py])         return points      def move(self):         self.y += self.vy         if self.y - self.radius &lt; 0 or self.y + self.radius &gt; WINDOW_HEIGHT:             self.vy = -self.vy          # gently wiggle each radial offset so that the blob keeps changing shape         self.offsets = [             max(0.4, min(1.6, off + random.uniform(-0.02, 0.02)))             for off in self.offsets         ]          self.canvas.coords(self.id, *self._get_points())      def distance_to(self, other):         return math.hypot(self.x - other.x, self.y - other.y)      def merge_with(self, other):         area1 = math.pi * (self.radius ** 2)         area2 = math.pi * (other.radius ** 2)         total = area1 + area2          self.radius = math.sqrt(total \/ math.pi)         self.offsets = [             (a + b) \/ 2 for a, b in zip(self.offsets, other.offsets)         ]         self.x = (self.x + other.x) \/ 2         self.y = (self.y + other.y) \/ 2  def simulate():     for blob in blobs:         blob.move()      for i in range(len(blobs)):         for j in range(i + 1, len(blobs)):             b1 = blobs[i]             b2 = blobs[j]             if b1.distance_to(b2) &lt; (b1.radius + b2.radius) * 0.65:                 b1.merge_with(b2)                 canvas.delete(b2.id)                 blobs.remove(b2)                 break      if random.random() &lt; 0.01 and len(blobs) &lt; 8:         new_blob = WaxBlob(canvas)         new_blob.radius = 15         blobs.append(new_blob)      root.after(30, simulate)  root = tk.Tk() root.title(\"Lava Lamp Simulator\") canvas = tk.Canvas(root, width=WINDOW_WIDTH, height=WINDOW_HEIGHT,                    bg=BACKGROUND_COLOR, highlightthickness=0) canvas.pack()  blobs = [WaxBlob(canvas) for _ in range(4)] root.after(30, simulate) root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_lavalamp_2.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import math import random from datetime import datetime  class WaxBlob:     def __init__(self, x, y, radius, canvas_width, canvas_height):         self.x = x         self.y = y         self.base_radius = radius         self.vx = random.uniform(-0.3, 0.3)         self.vy = random.uniform(-0.8, -0.2)         self.canvas_width = canvas_width         self.canvas_height = canvas_height         self.density = radius * 0.1         self.temperature = random.uniform(0.5, 1.0)         self.age = 0          # Shape deformation parameters         self.oscillation_phase = random.uniform(0, 2 * math.pi)         self.oscillation_speed = random.uniform(0.02, 0.08)         self.deformation_strength = random.uniform(0.1, 0.3)          # Control points for blob shape (relative to center)         self.num_control_points = 6         self.control_points = []         self.control_phases = []         self.control_speeds = []          for i in range(self.num_control_points):             angle = (2 * math.pi * i) \/ self.num_control_points             self.control_points.append({                 'base_angle': angle,                 'radius_offset': random.uniform(0.8, 1.2),                 'tangent_length': random.uniform(0.3, 0.7)             })             self.control_phases.append(random.uniform(0, 2 * math.pi))             self.control_speeds.append(random.uniform(0.01, 0.05))          # Flow deformation based on velocity         self.flow_memory = []         self.max_flow_memory = 5      @property     def radius(self):         return self.base_radius      def update(self):         self.age += 1          # Heat rises, cool sinks         heat_effect = (1.0 - self.y \/ self.canvas_height) * 0.3         buoyancy = (self.temperature + heat_effect - 0.5) * 0.02          # Gravity and buoyancy         self.vy += 0.005 - buoyancy          # Thermal currents         thermal_x = math.sin(self.y * 0.01 + self.age * 0.02) * 0.1         thermal_y = math.cos(self.x * 0.008 + self.age * 0.015) * 0.05          self.vx += thermal_x         self.vy += thermal_y          # Damping         self.vx *= 0.98         self.vy *= 0.995          # Store velocity for flow deformation         velocity_magnitude = math.sqrt(self.vx**2 + self.vy**2)         self.flow_memory.append(velocity_magnitude)         if len(self.flow_memory) &gt; self.max_flow_memory:             self.flow_memory.pop(0)          # Update position         self.x += self.vx         self.y += self.vy          # Update shape oscillations         self.oscillation_phase += self.oscillation_speed         for i in range(len(self.control_phases)):             self.control_phases[i] += self.control_speeds[i]          # Boundary collision         if self.x - self.base_radius &lt;= 0:             self.x = self.base_radius             self.vx = abs(self.vx) * 0.3 + random.uniform(0, 0.2)         elif self.x + self.base_radius &gt;= self.canvas_width:             self.x = self.canvas_width - self.base_radius             self.vx = -abs(self.vx) * 0.3 - random.uniform(0, 0.2)          if self.y - self.base_radius &lt;= 0:             self.y = self.base_radius             self.vy = abs(self.vy) * 0.4             self.temperature = max(0.2, self.temperature - 0.1)         elif self.y + self.base_radius &gt;= self.canvas_height:             self.y = self.canvas_height - self.base_radius             self.vy = -abs(self.vy) * 0.4             self.temperature = min(1.0, self.temperature + 0.2)      def get_bezier_control_points(self):         \"\"\"Generate control points for bezier curves that form the blob shape\"\"\"         avg_velocity = sum(self.flow_memory) \/ max(len(self.flow_memory), 1)         velocity_angle = math.atan2(self.vy, self.vx)          control_points = []          for i in range(self.num_control_points):             cp = self.control_points[i]              # Base position             base_angle = cp['base_angle']              # Oscillation deformation             oscillation = math.sin(self.oscillation_phase + i * 0.8) * self.deformation_strength              # Individual point oscillation             point_oscillation = math.sin(self.control_phases[i]) * 0.2              # Flow-based deformation             flow_factor = avg_velocity * 0.8             angle_diff = abs(base_angle - velocity_angle)             angle_diff = min(angle_diff, 2 * math.pi - angle_diff)              if angle_diff &lt; math.pi \/ 2:                 flow_deformation = flow_factor * (1 - angle_diff \/ (math.pi \/ 2)) * 0.4             else:                 flow_deformation = -flow_factor * 0.15              # Thermal deformation             thermal_deformation = self.temperature * 0.15 * math.sin(self.age * 0.03 + i * 1.2)              # Combined radius for this control point             radius_multiplier = (cp['radius_offset'] + oscillation + point_oscillation +                                flow_deformation + thermal_deformation)             radius_multiplier = max(0.4, min(1.8, radius_multiplier))              point_radius = self.base_radius * radius_multiplier              # Main control point             x = self.x + math.cos(base_angle) * point_radius             y = self.y + math.sin(base_angle) * point_radius              # Tangent control points for bezier curves             tangent_length = self.base_radius * cp['tangent_length'] * radius_multiplier             tangent_angle1 = base_angle - math.pi \/ 2             tangent_angle2 = base_angle + math.pi \/ 2              # Add some flow-based tangent adjustment             flow_tangent_adjust = avg_velocity * 0.3             tangent_length *= (1 + flow_tangent_adjust)              tx1 = x + math.cos(tangent_angle1) * tangent_length             ty1 = y + math.sin(tangent_angle1) * tangent_length             tx2 = x + math.cos(tangent_angle2) * tangent_length             ty2 = y + math.sin(tangent_angle2) * tangent_length              control_points.append({                 'point': (x, y),                 'tangent1': (tx1, ty1),                 'tangent2': (tx2, ty2)             })          return control_points      def distance_to(self, other):         return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)      def can_merge_with(self, other):         distance = self.distance_to(other)         merge_distance = (self.base_radius + other.base_radius) * 0.8         return distance &lt; merge_distance      def merge_with(self, other):         # Conservation of mass         total_area = math.pi * (self.base_radius**2 + other.base_radius**2)         new_radius = math.sqrt(total_area \/ math.pi)          # Weighted average         total_mass = self.base_radius**2 + other.base_radius**2         new_x = (self.x * self.base_radius**2 + other.x * other.base_radius**2) \/ total_mass         new_y = (self.y * self.base_radius**2 + other.y * other.base_radius**2) \/ total_mass         new_vx = (self.vx * self.base_radius**2 + other.vx * other.base_radius**2) \/ total_mass         new_vy = (self.vy * self.base_radius**2 + other.vy * other.base_radius**2) \/ total_mass         new_temp = (self.temperature + other.temperature) \/ 2          merged = WaxBlob(new_x, new_y, new_radius, self.canvas_width, self.canvas_height)         merged.vx = new_vx         merged.vy = new_vy         merged.temperature = new_temp          # Blend shape parameters         merged.oscillation_phase = (self.oscillation_phase + other.oscillation_phase) \/ 2         merged.oscillation_speed = (self.oscillation_speed + other.oscillation_speed) \/ 2         merged.deformation_strength = (self.deformation_strength + other.deformation_strength) \/ 2          return merged      def should_split(self):         if self.base_radius &gt; 40:             return random.random() &lt; 0.002         return False      def split(self):         split_ratio = random.uniform(0.3, 0.7)         area1 = math.pi * self.base_radius**2 * split_ratio         area2 = math.pi * self.base_radius**2 * (1 - split_ratio)          radius1 = math.sqrt(area1 \/ math.pi)         radius2 = math.sqrt(area2 \/ math.pi)          angle = random.uniform(0, 2 * math.pi)         offset = (radius1 + radius2) * 0.6          blob1 = WaxBlob(             self.x + math.cos(angle) * offset,             self.y + math.sin(angle) * offset,             radius1, self.canvas_width, self.canvas_height         )         blob2 = WaxBlob(             self.x - math.cos(angle) * offset,             self.y - math.sin(angle) * offset,             radius2, self.canvas_width, self.canvas_height         )          blob1.vx = self.vx + random.uniform(-0.5, 0.5)         blob1.vy = self.vy + random.uniform(-0.5, 0.5)         blob2.vx = self.vx + random.uniform(-0.5, 0.5)         blob2.vy = self.vy + random.uniform(-0.5, 0.5)          blob1.temperature = self.temperature + random.uniform(-0.1, 0.1)         blob2.temperature = self.temperature + random.uniform(-0.1, 0.1)          # Vary shape parameters         blob1.oscillation_speed = self.oscillation_speed + random.uniform(-0.02, 0.02)         blob2.oscillation_speed = self.oscillation_speed + random.uniform(-0.02, 0.02)         blob1.deformation_strength = self.deformation_strength + random.uniform(-0.1, 0.1)         blob2.deformation_strength = self.deformation_strength + random.uniform(-0.1, 0.1)          return [blob1, blob2]  class LavaLampSimulator:     def __init__(self, root):         self.root = root         self.root.title(\"Lava Lamp Simulator - Bezier Blobs\")         self.root.geometry(\"400x600\")         self.root.configure(bg='black')          # Create canvas         self.canvas = tk.Canvas(root, width=380, height=580, bg='#1a0d0d', highlightthickness=0)         self.canvas.pack(pady=10)          # Initialize blobs         self.blobs = []         self.create_initial_blobs()          # Start animation         self.animate()      def create_initial_blobs(self):         for _ in range(6):             x = random.uniform(50, 330)             y = random.uniform(100, 500)             radius = random.uniform(20, 35)             blob = WaxBlob(x, y, radius, 380, 580)             self.blobs.append(blob)      def get_blob_color(self, blob):         temp = blob.temperature         base_red = int(255 * (0.7 + temp * 0.3))         base_green = int(100 * temp)         base_blue = int(50 * temp)          height_factor = 1.0 - (blob.y \/ 580)         red = min(255, int(base_red * (0.8 + height_factor * 0.2)))         green = min(255, int(base_green * (0.8 + height_factor * 0.2)))         blue = min(255, int(base_blue * (0.8 + height_factor * 0.2)))          return f\"#{red:02x}{green:02x}{blue:02x}\"      def bezier_point(self, t, p0, p1, p2, p3):         \"\"\"Calculate a point on a cubic bezier curve\"\"\"         u = 1 - t         return (             u**3 * p0[0] + 3 * u**2 * t * p1[0] + 3 * u * t**2 * p2[0] + t**3 * p3[0],             u**3 * p0[1] + 3 * u**2 * t * p1[1] + 3 * u * t**2 * p2[1] + t**3 * p3[1]         )      def create_bezier_blob_outline(self, blob, resolution=20):         \"\"\"Create a smooth blob outline using bezier curves\"\"\"         control_points = blob.get_bezier_control_points()         outline_points = []          num_segments = len(control_points)          for i in range(num_segments):             # Current and next control point             curr = control_points[i]             next_i = (i + 1) % num_segments             next_cp = control_points[next_i]              # Define bezier curve from current point to next point             p0 = curr['point']             p1 = curr['tangent2']  # Outgoing tangent from current point             p2 = next_cp['tangent1']  # Incoming tangent to next point             p3 = next_cp['point']              # Generate points along the bezier curve             for j in range(resolution):                 if i == num_segments - 1 and j == resolution - 1:                     # Skip the last point to avoid duplication                     break                  t = j \/ resolution                 point = self.bezier_point(t, p0, p1, p2, p3)                 outline_points.extend([point[0], point[1]])          return outline_points      def draw_blob(self, blob):         color = self.get_blob_color(blob)          try:             # Get bezier outline points             outline_points = self.create_bezier_blob_outline(blob)              if len(outline_points) &gt;= 6:                 # Draw the main blob                 self.canvas.create_polygon(outline_points, fill=color, outline='', smooth=True)                  # Create highlight with scaled bezier shape                 highlight_points = []                 highlight_scale = 0.4                 highlight_center_x = blob.x - blob.base_radius * 0.3                 highlight_center_y = blob.y - blob.base_radius * 0.3                  for i in range(0, len(outline_points), 2):                     orig_x = outline_points[i]                     orig_y = outline_points[i + 1]                      # Vector from highlight center to point                     vec_x = orig_x - highlight_center_x                     vec_y = orig_y - highlight_center_y                      # Scale toward highlight center                     new_x = highlight_center_x + vec_x * highlight_scale                     new_y = highlight_center_y + vec_y * highlight_scale                      highlight_points.extend([new_x, new_y])                  # Lighter color for highlight                 r = min(255, int(color[1:3], 16) + 40)                 g = min(255, int(color[3:5], 16) + 20)                 b = min(255, int(color[5:7], 16) + 10)                 highlight_color = f\"#{r:02x}{g:02x}{b:02x}\"                  if len(highlight_points) &gt;= 6:                     self.canvas.create_polygon(highlight_points, fill=highlight_color,                                              outline='', smooth=True)             else:                 self.draw_fallback_circle(blob, color)          except (tk.TclError, ValueError):             # Fallback to circle if bezier fails             self.draw_fallback_circle(blob, color)      def draw_fallback_circle(self, blob, color):         \"\"\"Fallback method to draw a simple circle\"\"\"         x1 = blob.x - blob.base_radius         y1 = blob.y - blob.base_radius         x2 = blob.x + blob.base_radius         y2 = blob.y + blob.base_radius         self.canvas.create_oval(x1, y1, x2, y2, fill=color, outline='')      def update_physics(self):         # Update all blobs         for blob in self.blobs:             blob.update()          # Handle merging         merged_blobs = []         used_indices = set()          for i, blob1 in enumerate(self.blobs):             if i in used_indices:                 continue              merged = False             for j, blob2 in enumerate(self.blobs[i+1:], i+1):                 if j in used_indices:                     continue                  if blob1.can_merge_with(blob2):                     new_blob = blob1.merge_with(blob2)                     merged_blobs.append(new_blob)                     used_indices.add(i)                     used_indices.add(j)                     merged = True                     break              if not merged:                 merged_blobs.append(blob1)          self.blobs = merged_blobs          # Handle splitting         new_blobs = []         for blob in self.blobs:             if blob.should_split():                 split_blobs = blob.split()                 new_blobs.extend(split_blobs)             else:                 new_blobs.append(blob)          self.blobs = new_blobs          # Maintain minimum number of blobs         if len(self.blobs) &lt; 3:             x = random.uniform(50, 330)             y = random.uniform(400, 550)             radius = random.uniform(15, 25)             new_blob = WaxBlob(x, y, radius, 380, 580)             new_blob.temperature = 0.8             self.blobs.append(new_blob)      def animate(self):         # Clear canvas         self.canvas.delete(\"all\")          # Update physics         self.update_physics()          # Draw all blobs         for blob in self.blobs:             self.draw_blob(blob)          # Schedule next frame         self.root.after(50, self.animate)  # Add the missing method to WaxBlob class def get_bezier_control_points(self):     \"\"\"Generate control points for bezier curves that form the blob shape\"\"\"     avg_velocity = sum(self.flow_memory) \/ max(len(self.flow_memory), 1)     velocity_angle = math.atan2(self.vy, self.vx)      control_points = []      for i in range(self.num_control_points):         cp = self.control_points[i]          # Base position         base_angle = cp['base_angle']          # Oscillation deformation         oscillation = math.sin(self.oscillation_phase + i * 0.8) * self.deformation_strength          # Individual point oscillation         point_oscillation = math.sin(self.control_phases[i]) * 0.2          # Flow-based deformation         flow_factor = avg_velocity * 0.8         angle_diff = abs(base_angle - velocity_angle)         angle_diff = min(angle_diff, 2 * math.pi - angle_diff)          if angle_diff &lt; math.pi \/ 2:             flow_deformation = flow_factor * (1 - angle_diff \/ (math.pi \/ 2)) * 0.4         else:             flow_deformation = -flow_factor * 0.15          # Thermal deformation         thermal_deformation = self.temperature * 0.15 * math.sin(self.age * 0.03 + i * 1.2)          # Combined radius for this control point         radius_multiplier = (cp['radius_offset'] + oscillation + point_oscillation +                            flow_deformation + thermal_deformation)         radius_multiplier = max(0.4, min(1.8, radius_multiplier))          point_radius = self.base_radius * radius_multiplier          # Main control point position         x = self.x + math.cos(base_angle) * point_radius         y = self.y + math.sin(base_angle) * point_radius          # Calculate tangent control points for smooth bezier curves         tangent_length = self.base_radius * cp['tangent_length'] * radius_multiplier * 0.5          # Tangent direction (perpendicular to radius, with some variation)         tangent_base_angle = base_angle + math.pi \/ 2         tangent_variation = math.sin(self.control_phases[i] * 2) * 0.3         tangent_angle = tangent_base_angle + tangent_variation          # Flow influence on tangents         flow_influence = avg_velocity * 0.4         flow_angle_influence = math.sin(base_angle - velocity_angle) * flow_influence         tangent_angle += flow_angle_influence          # Tangent control points (before and after the main point)         tx1 = x - math.cos(tangent_angle) * tangent_length         ty1 = y - math.sin(tangent_angle) * tangent_length         tx2 = x + math.cos(tangent_angle) * tangent_length         ty2 = y + math.sin(tangent_angle) * tangent_length          control_points.append({             'point': (x, y),             'tangent1': (tx1, ty1),             'tangent2': (tx2, ty2)         })      return control_points  # Attach the method to the WaxBlob class WaxBlob.get_bezier_control_points = get_bezier_control_points  if __name__ == \"__main__\":     root = tk.Tk()     app = LavaLampSimulator(root)     root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u0441\u043d\u0435\u0436\u043d\u043e\u0433\u043e \u0448\u0430\u0440\u0430<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442:<\/p>\n<blockquote>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043d\u0430 Python, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e tkinter \u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443. \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u2014 \u044d\u0442\u043e \u201c\u0441\u043d\u0435\u0436\u043d\u044b\u0439 \u0448\u0430\u0440\u201d \u0441 \u0441\u0438\u043d\u0438\u043c \u0444\u043e\u043d\u043e\u043c \u0438 \u0431\u0435\u043b\u044b\u043c \u201c\u0441\u043d\u0435\u0433\u043e\u043c\u201d \u0432\u043d\u0443\u0442\u0440\u0438. \u041a\u043e\u0433\u0434\u0430 \u043e\u043a\u043d\u043e \u0434\u0432\u0438\u0433\u0430\u044e\u0442 \u0438 \u201c\u0442\u0440\u044f\u0441\u0443\u0442\u201d, \u0441\u043d\u0435\u0433 \u0442\u043e\u0436\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u0442\u0430\u043a, \u043a\u0430\u043a \u044d\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u043d\u0435\u0436\u043d\u043e\u043c \u0448\u0430\u0440\u0435. \u0427\u0435\u043c \u0441\u0438\u043b\u044c\u043d\u0435\u0435 \u0442\u0440\u044f\u0441\u043a\u0430, \u0442\u0435\u043c \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0434\u0432\u0438\u0436\u0435\u0442\u0441\u044f \u0441\u043d\u0435\u0433. \u0421\u043d\u0435\u0436\u0438\u043d\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e \u0441\u043f\u0438\u0440\u0430\u043b\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u043a\u043d\u0430, \u0430 \u043d\u0435 \u043b\u0435\u0442\u0430\u0442\u044c, \u043a\u0430\u043a \u043e\u0442\u0441\u043a\u0430\u043a\u0438\u0432\u0430\u044e\u0449\u0438\u0435 \u043f\u0438\u043d\u0433-\u043f\u043e\u043d\u0433\u043e\u0432\u044b\u0435 \u043c\u044f\u0447\u0438.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u0423 Claude \u0431\u044b\u043b \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u0443\u0441\u043f\u0435\u0445: \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0438 \u043e\u043a\u043d\u0430 \u00ab\u0441\u043d\u0435\u0436\u0438\u043d\u043a\u0438\u00bb \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u00ab\u0442\u0440\u044f\u0441\u043b\u0438\u0441\u044c\u00bb, \u043d\u043e \u0432\u0435\u043b\u0438 \u0441\u0435\u0431\u044f \u0441\u043a\u043e\u0440\u0435\u0435 \u043a\u0430\u043a \u043f\u0438\u043d\u0433-\u043f\u043e\u043d\u0433\u043e\u0432\u044b\u0435 \u043c\u044f\u0447\u0438 \u0432 \u043a\u0430\u0440\u0442\u043e\u043d\u043d\u043e\u0439 \u043a\u043e\u0440\u043e\u0431\u043a\u0435, \u0430 \u043d\u0435 \u043a\u0430\u043a \u0441\u043d\u0435\u0433 \u0432 \u0448\u0430\u0440\u0435. \u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0438 \u0441\u0438\u043b\u0430 \u0442\u0440\u044f\u0441\u043a\u0438 \u043d\u0435 \u0432\u043b\u0438\u044f\u043b\u0438 \u043d\u0430 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435: \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0430\u044f \u0438 \u0431\u044b\u0441\u0442\u0440\u0430\u044f \u0442\u0440\u044f\u0441\u043a\u0430 \u0434\u0430\u0432\u0430\u043b\u0438 \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u044d\u0444\u0444\u0435\u043a\u0442.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/698\/584\/28d\/69858428d056b0daef6a2970f6dff0f9.png\" width=\"1524\" height=\"814\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/698\/584\/28d\/69858428d056b0daef6a2970f6dff0f9.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/698\/584\/28d\/69858428d056b0daef6a2970f6dff0f9.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<details class=\"spoiler\">\n<summary>fail_snowglobe_1.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import random import time  WIDTH, HEIGHT = 400, 400 NUM_SNOWFLAKES = 200 GRAVITY = 0.2 FRICTION = 0.98  class Snowflake:     def __init__(self, canvas):         self.canvas = canvas         self.x = random.uniform(0, WIDTH)         self.y = random.uniform(0, HEIGHT)         self.vx = 0         self.vy = 0         self.id = canvas.create_oval(self.x, self.y, self.x+2, self.y+2, fill=\"white\", outline=\"\")      def update(self, shake_x, shake_y):         # Apply shake force to the velocity         self.vx += shake_x         self.vy += shake_y          # Apply gravity         self.vy += GRAVITY          # Move flake         self.x += self.vx         self.y += self.vy          # Stay in bounds (wrap horizontally, bounce vertically)         if self.x &lt; 0:             self.x = WIDTH         elif self.x &gt; WIDTH:             self.x = 0          # Top boundary: wrap to bottom         if self.y &lt; 0:             self.y = HEIGHT          # Bottom boundary: bounce         if self.y &gt; HEIGHT:             self.y = HEIGHT             self.vy *= -0.5  # bounce a bit          # Friction to slow flakes gradually         self.vx *= FRICTION         self.vy *= FRICTION          self.canvas.coords(self.id, self.x, self.y, self.x+2, self.y+2)  class SnowGlobe:     def __init__(self, root):         self.root = root         self.canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg=\"skyblue\")         self.canvas.pack()         self.snowflakes = [Snowflake(self.canvas) for _ in range(NUM_SNOWFLAKES)]          # Track window motion         self.prev_x = root.winfo_rootx()         self.prev_y = root.winfo_rooty()         self.shake_x = 0         self.shake_y = 0          root.bind(\"&lt;Configure&gt;\", self.on_configure)         self.update_animation()      def on_configure(self, event):         cur_x = self.root.winfo_rootx()         cur_y = self.root.winfo_rooty()         dx = cur_x - self.prev_x         dy = cur_y - self.prev_y         self.prev_x = cur_x         self.prev_y = cur_y          # A little scaling so larger movements cause bigger shake         self.shake_x = dx * 0.2         self.shake_y = dy * 0.2      def update_animation(self):         # Apply shake to each snowflake         for snow in self.snowflakes:             snow.update(self.shake_x, self.shake_y)          # shake \u00e2\u20ac\u0153dampens out\u00e2\u20ac\u009d over time         self.shake_x *= 0.9         self.shake_y *= 0.9          self.root.after(16, self.update_animation)  if __name__ == \"__main__\":     root = tk.Tk()     root.title(\"Snow Globe\")     globe = SnowGlobe(root)     root.mainloop()<\/code><\/pre>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>fail_snowglobe_2.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk import random import math import time  class SnowGlobe:     def __init__(self):         self.root = tk.Tk()         self.root.title(\"Snow Globe\")         self.root.geometry(\"400x400\")         self.root.configure(bg='lightblue')                  # Canvas for drawing snow         self.canvas = tk.Canvas(self.root, width=380, height=380, bg='lightblue', highlightthickness=0)         self.canvas.pack(padx=10, pady=10)                  # Snow particles         self.snowflakes = []         self.num_snowflakes = 100                  # Window position tracking for shake detection         self.last_x = self.root.winfo_x()         self.last_y = self.root.winfo_y()         self.last_time = time.time()         self.velocity_x = 0         self.velocity_y = 0         self.shake_intensity = 0                  # Physics constants         self.gravity = 0.1         self.friction = 0.98         self.bounce_damping = 0.7                  self.create_snowflakes()         self.animate()                  # Bind window movement events         self.root.bind('&lt;Configure&gt;', self.on_window_move)              def create_snowflakes(self):         \"\"\"Create initial snowflakes with random positions and properties\"\"\"         for _ in range(self.num_snowflakes):             snowflake = {                 'x': random.uniform(5, 375),                 'y': random.uniform(5, 375),                 'vx': 0,                 'vy': 0,                 'size': random.uniform(2, 6),                 'id': None             }             self.snowflakes.append(snowflake)          def on_window_move(self, event):         \"\"\"Track window movement to detect shaking\"\"\"         if event.widget == self.root:             current_time = time.time()             current_x = self.root.winfo_x()             current_y = self.root.winfo_y()                          # Calculate time difference             dt = current_time - self.last_time             if dt &gt; 0:                 # Calculate velocity based on position change                 self.velocity_x = (current_x - self.last_x) \/ dt                 self.velocity_y = (current_y - self.last_y) \/ dt                                  # Calculate shake intensity based on velocity magnitude                 velocity_magnitude = math.sqrt(self.velocity_x**2 + self.velocity_y**2)                 self.shake_intensity = min(velocity_magnitude \/ 100, 10)  # Cap at 10                                  # Apply shake force to snowflakes                 self.apply_shake_force()                                  self.last_x = current_x                 self.last_y = current_y                 self.last_time = current_time          def apply_shake_force(self):         \"\"\"Apply forces to snowflakes based on window shaking\"\"\"         if self.shake_intensity &gt; 0.1:  # Only apply if significant movement             for snowflake in self.snowflakes:                 # Add random component to make it more realistic                 force_x = (self.velocity_x \/ 50) + random.uniform(-1, 1) * self.shake_intensity                 force_y = (self.velocity_y \/ 50) + random.uniform(-1, 1) * self.shake_intensity                                  snowflake['vx'] += force_x                 snowflake['vy'] += force_y          def update_snowflakes(self):         \"\"\"Update snowflake positions and handle physics\"\"\"         for snowflake in self.snowflakes:             # Apply gravity (always pulling down)             snowflake['vy'] += self.gravity                          # Update positions             snowflake['x'] += snowflake['vx']             snowflake['y'] += snowflake['vy']                          # Apply friction             snowflake['vx'] *= self.friction             snowflake['vy'] *= self.friction                          # Boundary collisions with bounce             if snowflake['x'] &lt;= snowflake['size']:                 snowflake['x'] = snowflake['size']                 snowflake['vx'] = -snowflake['vx'] * self.bounce_damping             elif snowflake['x'] &gt;= 380 - snowflake['size']:                 snowflake['x'] = 380 - snowflake['size']                 snowflake['vx'] = -snowflake['vx'] * self.bounce_damping                          if snowflake['y'] &lt;= snowflake['size']:                 snowflake['y'] = snowflake['size']                 snowflake['vy'] = -snowflake['vy'] * self.bounce_damping             elif snowflake['y'] &gt;= 380 - snowflake['size']:                 snowflake['y'] = 380 - snowflake['size']                 snowflake['vy'] = -snowflake['vy'] * self.bounce_damping                 # Extra damping when hitting bottom (like settling)                 snowflake['vx'] *= 0.8          def draw_snowflakes(self):         \"\"\"Draw all snowflakes on the canvas\"\"\"         self.canvas.delete(\"snowflake\")                  for snowflake in self.snowflakes:             x, y, size = snowflake['x'], snowflake['y'], snowflake['size']                          # Draw snowflake as a white circle             self.canvas.create_oval(                 x - size, y - size, x + size, y + size,                 fill='white', outline='white', tags=\"snowflake\"             )                          # Add a sparkle effect for larger snowflakes             if size &gt; 4:                 self.canvas.create_oval(                     x - 1, y - 1, x + 1, y + 1,                     fill='lightcyan', outline='lightcyan', tags=\"snowflake\"                 )          def animate(self):         \"\"\"Main animation loop\"\"\"         self.update_snowflakes()         self.draw_snowflakes()                  # Gradually reduce shake intensity over time         self.shake_intensity *= 0.95                  # Schedule next frame         self.root.after(16, self.animate)  # ~60 FPS          def run(self):         \"\"\"Start the snow globe application\"\"\"         self.root.mainloop()  if __name__ == \"__main__\":     snow_globe = SnowGlobe()     snow_globe.run()<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>\u0420\u0443\u0441\u0441\u043a\u043e\u044f\u0437\u044b\u0447\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u043e Python<\/h2>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e91\/769\/27a\/e9176927a1a3e8c3ff61d923d1300157.png\" width=\"1560\" height=\"334\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/e91\/769\/27a\/e9176927a1a3e8c3ff61d923d1300157.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e91\/769\/27a\/e9176927a1a3e8c3ff61d923d1300157.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0414\u0440\u0443\u0437\u044c\u044f! \u042d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e \u043f\u0435\u0440\u0435\u0432\u0435\u043b\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u00a0<a href=\"https:\/\/t.me\/+wyxutDzVYHcxNzE6\" rel=\"noopener noreferrer nofollow\">Python for Devs<\/a>\u00a0\u2014 \u043a\u0430\u043d\u0430\u043b\u0430, \u0433\u0434\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c \u0432\u044b\u0445\u043e\u0434\u044f\u0442 \u0441\u0430\u043c\u044b\u0435 \u0441\u0432\u0435\u0436\u0438\u0435 \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b \u043e Python \u0438 \u0435\u0433\u043e \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435.\u00a0<a href=\"https:\/\/t.me\/+wyxutDzVYHcxNzE6\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435\u0441\u044c<\/a>, \u0447\u0442\u043e\u0431\u044b \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/944698\/\"> https:\/\/habr.com\/ru\/articles\/944698\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041a\u043e\u043c\u0430\u043d\u0434\u0430 <a href=\"https:\/\/t.me\/+wyxutDzVYHcxNzE6\" rel=\"noopener noreferrer nofollow\">Python for Devs<\/a> \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0441\u0442\u0430\u0442\u044c\u0438 \u042d\u043ba \u0421\u0432\u0435\u0439\u0433\u0430\u0440\u0442\u0430 \u043e \u043d\u0435\u0443\u0434\u0430\u0447\u043d\u044b\u0445 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u0445 \u0441 vibe coding. \u0412\u0441\u0435 \u0433\u043e\u0432\u043e\u0440\u044f\u0442, \u0447\u0442\u043e \u0418\u0418 \u0443\u0436\u0435 \u0443\u043c\u0435\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u0441\u0442\u043e\u0438\u0442 \u0447\u0443\u0442\u044c \u043e\u0442\u043a\u043b\u043e\u043d\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u2014 \u0438 \u0432\u0441\u0451 \u0438\u0434\u0451\u0442 \u043d\u0430\u043f\u0435\u0440\u0435\u043a\u043e\u0441\u044f\u043a. \u041a\u0430\u0440\u0442\u043e\u0444\u0435\u043b\u044c\u043d\u0430\u044f \u0410\u0444\u0440\u0438\u043a\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u043a\u0430\u0440\u0442\u044b, \u043f\u0438\u043d\u0431\u043e\u043b, \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439\u0441\u044f \u0432 \u043f\u0438\u043d\u0433-\u043f\u043e\u043d\u0433, \u0438 \u0441\u0447\u0451\u0442\u044b \u0441 \u043e\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0447\u0438\u0441\u043b\u0430\u043c\u0438 \u2014 \u0430\u0432\u0442\u043e\u0440 \u0441\u043e\u0431\u0440\u0430\u043b \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u043e\u0432\u0430\u043b\u043e\u0432 \u0441 vibe coding.<\/p>\n<hr\/>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u043d\u0435\u0434\u0435\u043b\u044e \u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043b \u0441 vibe coding: \u043f\u0440\u043e\u0441\u0438\u043b LLM-\u043c\u043e\u0434\u0435\u043b\u0438 \u0432\u0440\u043e\u0434\u0435 ChatGPT, Claude \u0438 Gemini \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0442\u0430\u043a, \u0431\u0443\u0434\u0442\u043e \u0443 \u043c\u0435\u043d\u044f \u043d\u0435\u0442 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043d\u0430\u0432\u044b\u043a\u043e\u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. LLM \u043b\u0435\u0433\u043a\u043e \u0440\u0435\u0448\u0430\u044e\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0430 \u043a\u043e\u0434\u0438\u043d\u0433 \u0438\u043b\u0438 \u0441\u043e\u0431\u0435\u0441\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b. \u041d\u043e \u043c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u0430\u043b\u0435\u043a\u043e \u043e\u043d\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b \u0437\u0430\u0439\u0442\u0438, \u0435\u0441\u043b\u0438 \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0438\u0445 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0438 \u043a\u0430\u043a\u0438\u0435 \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0435 \u0441\u0431\u043e\u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f. \u0412 \u0440\u043e\u043b\u0438 \u00ab\u043d\u0435\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u00bb \u044f \u043c\u043e\u0433 \u0431\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0431\u0430\u0433\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044f \u0438\u0445 LLM. \u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u044f \u0432\u044b\u0431\u0440\u0430\u043b \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 Python, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0438 \u043f\u0430\u043a\u0435\u0442 <code>tkinter<\/code> \u0434\u043b\u044f GUI. \u0412 \u044d\u0442\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u043e\u0431 \u044d\u0442\u0438\u0445 \u043f\u0440\u043e\u0432\u0430\u043b\u0430\u0445 \u2014 \u043e \u0442\u0435\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445, \u0433\u0434\u0435 \u0418\u0418 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f.<\/p>\n<p>\u041c\u0435\u043d\u044f \u043d\u0435 \u0432\u043e\u043b\u043d\u0443\u0435\u0442 \u0438\u0437\u044b\u0441\u043a\u0430\u043d\u043d\u044b\u0439 \u0438\u043b\u0438 \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 (\u0432 \u043a\u043e\u043d\u0446\u0435 \u043a\u043e\u043d\u0446\u043e\u0432, \u0442\u0443\u0442 \u0432\u0441\u0451 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043e tkinter). \u0412\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0435\u0437 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a. \u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u0432 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b ChatGPT 5, Gemini 2.5 Pro \u0438 Claude Sonnet 4.<\/p>\n<p>\u0412 \u0442\u0435\u043a\u0441\u0442\u0435 \u044f \u043f\u0440\u0438\u0432\u043e\u0436\u0443 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c, \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 LLM. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u0443\u0434\u0430\u0441\u0442\u0441\u044f \u0434\u043e\u0432\u0435\u0441\u0442\u0438 \u0434\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043b\u044e\u0431\u0443\u044e \u0438\u0437 \u044d\u0442\u0438\u0445 \u0438\u0434\u0435\u0439, \u043c\u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0443\u0437\u043d\u0430\u0442\u044c \u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445:\u00a0<a href=\"mailto:al@inventwithpython.com\" rel=\"noopener noreferrer nofollow\">al@inventwithpython.com<\/a>.<\/p>\n<h2>\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u043d\u0435\u0443\u0434\u0430\u0447 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u043e\u0442 LLM<\/h2>\n<p>\u041e\u0431\u044b\u0447\u043d\u043e LLM \u043d\u0435 \u0443\u0434\u0430\u0432\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u043e\u0444\u0442 \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438:<\/p>\n<ul>\n<li>\n<p>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043d\u0435\u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u041b\u044e\u0431\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043d\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0442\u043d\u0438 \u0440\u0430\u0437 (\u0422\u0435\u0442\u0440\u0438\u0441, \u0441\u0435\u043a\u0443\u043d\u0434\u043e\u043c\u0435\u0440, \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u043b \u0438 \u0442. \u043f.).<\/p>\n<\/li>\n<li>\n<p>\u0422\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0438\u043b\u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a. LLM \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 \u0442\u0435\u043a\u0441\u0442, \u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438 \u0438\u043b\u0438 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u043e\u0439 \u0443 \u043d\u0438\u0445 \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0430\u0437\u0432\u0430\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0445\u043e\u0436\u0438\u0435, \u043d\u043e \u043d\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u044b\u0435 \u0442\u0438\u043f\u043e\u0432\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0438\u043d\u0431\u043e\u043b \u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u043f\u0438\u043d\u0433-\u043f\u043e\u043d\u0433. \u0415\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u044b \u0430\u043c\u043e\u0440\u0444\u043d\u044b\u0435 \u043f\u044f\u0442\u043d\u0430, \u043a\u0430\u043a \u0432 \u043b\u0430\u0432\u043e\u0432\u043e\u0439 \u043b\u0430\u043c\u043f\u0435, LLM \u0440\u0438\u0441\u0443\u044e\u0442 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u0440\u0443\u0433\u0438. \u041c\u043e\u0434\u0435\u043b\u0438 \u0441\u043a\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043a \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u043c, \u043d\u043e \u043d\u0435\u0442\u043e\u0447\u043d\u044b\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c, \u0438\u043d\u043e\u0433\u0434\u0430 \u0434\u0430\u0436\u0435 \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u043f\u0440\u044f\u043c\u044b\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e\u0433\u043e.<\/p>\n<\/li>\n<\/ul>\n<h2>\u0421\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u0432 \u0441 vibe coding:<\/h2>\n<ul>\n<li>\n<p>\u0412\u0438\u043a\u0442\u043e\u0440\u0438\u043d\u0430 \u043f\u043e \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0441\u0442\u0440\u0430\u043d \u0410\u0444\u0440\u0438\u043a\u0438<\/p>\n<\/li>\n<li>\n<p>\u0418\u0433\u0440\u0430 \u00ab\u041f\u0438\u043d\u0431\u043e\u043b\u00bb<\/p>\n<\/li>\n<li>\n<p>\u0413\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u043a\u0440\u0443\u0433\u043e\u0432\u044b\u0445 \u043b\u0430\u0431\u0438\u0440\u0438\u043d\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0418\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u0438\u0442\u0430\u0439\u0441\u043a\u0438\u0435 \u0441\u0447\u0435\u0442\u044b<\/p>\n<\/li>\n<li>\n<p>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u043a\u043e\u0434\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043c\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0433\u0435\u043d\u0435\u0430\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0435\u0440\u0435\u0432\u044c\u0435\u0432<\/p>\n<\/li>\n<li>\n<p>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u043b\u0430\u0432\u043e\u0432\u043e\u0439 \u043b\u0430\u043c\u043f\u044b<\/p>\n<\/li>\n<li>\n<p>\u0421\u0438\u043c\u0443\u043b\u044f\u0442\u043e\u0440 \u0441\u043d\u0435\u0436\u043d\u043e\u0433\u043e \u0448\u0430\u0440\u0430<\/p>\n<\/li>\n<\/ul>\n<h2>\u0412\u0438\u043a\u0442\u043e\u0440\u0438\u043d\u0430 \u043f\u043e \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0441\u0442\u0440\u0430\u043d \u0410\u0444\u0440\u0438\u043a\u0438<\/h2>\n<p>\u041f\u0440\u043e\u043c\u043f\u0442:<\/p>\n<blockquote>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043d\u0430 Python \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c tkinter \u0434\u043b\u044f GUI \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0438\u0437 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0410\u0444\u0440\u0438\u043a\u0438 \u0431\u0435\u0437 \u0433\u0440\u0430\u043d\u0438\u0446 \u043c\u0435\u0436\u0434\u0443 \u0441\u0442\u0440\u0430\u043d\u0430\u043c\u0438, \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0442\u0443\u0440 \u0410\u0444\u0440\u0438\u043a\u0438. \u041e\u043d\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0430\u0444\u0440\u0438\u043a\u0430\u043d\u0441\u043a\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u044b, \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u0435\u043d \u043a\u043b\u0438\u043a\u043d\u0443\u0442\u044c \u043f\u043e \u043a\u0430\u0440\u0442\u0435. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0443\u0440 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u044b \u043d\u0430 \u043a\u0430\u0440\u0442\u0435 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434, \u043f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u044b. \u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043d\u0435\u0442 \u043e\u0447\u043a\u043e\u0432 \u0438\u043b\u0438 \u0431\u0430\u043b\u043b\u043e\u0432. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442, \u043f\u043e\u043f\u0430\u043b \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0441\u0442\u0440\u0430\u043d\u044b, \u0438\u043b\u0438 \u043d\u0435\u0442 (\u043e\u043d \u0438 \u0442\u0430\u043a \u0443\u0432\u0438\u0434\u0438\u0442 \u044d\u0442\u043e, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0443\u0440 \u0441\u0442\u0440\u0430\u043d\u044b). \u0427\u0442\u043e\u0431\u044b \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c, \u0442\u043e\u0447\u043a\u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043d\u0435\u0442: \u0432\u0438\u043a\u0442\u043e\u0440\u0438\u043d\u0430 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0437\u0430\u043a\u0440\u043e\u0435\u0442 \u043e\u043a\u043d\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<\/blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: LLM \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u00ab\u0437\u0430\u0431\u044b\u0432\u0430\u044e\u0442\u00bb, \u0447\u0442\u043e \u041c\u0430\u0434\u0430\u0433\u0430\u0441\u043a\u0430\u0440 \u2014 \u0447\u0430\u0441\u0442\u044c \u0410\u0444\u0440\u0438\u043a\u0438, \u0435\u0441\u043b\u0438 \u0438\u043c \u043e\u0431 \u044d\u0442\u043e\u043c \u043d\u0435 \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c. \u0415\u0449\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e LLM \u0441\u0447\u0438\u0442\u0430\u044e\u0442, \u0431\u0443\u0434\u0442\u043e \u0410\u0444\u0440\u0438\u043a\u0430 \u043f\u043e \u0444\u043e\u0440\u043c\u0435 \u043a\u0430\u043a \u043a\u0430\u0440\u0442\u043e\u0448\u043a\u0430.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u042f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u043b \u043d\u0430 \u043d\u0435\u0447\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043d\u0430 <a href=\"https:\/\/www.sporcle.com\/games\/kfastic\/countries-of-africa-without-outlines\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u0443 \u0432\u0438\u043a\u0442\u043e\u0440\u0438\u043d\u0443 \u043f\u043e \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0438<\/a>: \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0410\u0444\u0440\u0438\u043a\u0430 \u0431\u0435\u0437 \u0433\u0440\u0430\u043d\u0438\u0446 \u0441\u0442\u0440\u0430\u043d, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e\u0442 \u043a\u043b\u0438\u043a\u043d\u0443\u0442\u044c \u043f\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0435.<\/p>\n<p>\u041e\u0434\u043d\u0430 \u0438\u0437 LLM \u0432\u044b\u0434\u0430\u043b\u0430 \u0447\u0442\u043e-\u0442\u043e \u043e\u0442\u0434\u0430\u043b\u0435\u043d\u043d\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043d\u0430 \u0410\u0444\u0440\u0438\u043a\u0443 \u0438 \u0434\u0430\u0436\u0435 \u043d\u0430\u0440\u0438\u0441\u043e\u0432\u0430\u043b\u0430 \u041c\u0430\u0434\u0430\u0433\u0430\u0441\u043a\u0430\u0440 (\u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u044f \u043d\u0430\u043f\u043e\u043c\u043d\u0438\u043b, \u0447\u0442\u043e \u043e\u043d \u2014 \u0447\u0430\u0441\u0442\u044c \u0410\u0444\u0440\u0438\u043a\u0438). \u041d\u043e \u0444\u043e\u0440\u043c\u044b \u0441\u0442\u0440\u0430\u043d \u0431\u044b\u043b\u0438&#8230; \u043c\u044f\u0433\u043a\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u043d\u0435\u0442\u043e\u0447\u043d\u044b\u043c\u0438. \u042f \u043f\u0440\u043e\u0441\u0438\u043b \u0438\u0445 \u043d\u0430\u0439\u0442\u0438 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 SVG-\u0444\u0430\u0439\u043b\u044b \u043a\u0430\u0440\u0442 \u0410\u0444\u0440\u0438\u043a\u0438. \u041e\u043d\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u043b\u0438, \u0447\u0442\u043e \u043d\u0430\u0448\u043b\u0438, \u0430 \u0437\u0430\u0442\u0435\u043c \u0441\u043d\u043e\u0432\u0430 \u0440\u0438\u0441\u043e\u0432\u0430\u043b\u0438 \u043a\u0430\u0440\u0442\u043e\u0448\u043a\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>fail_africa_1.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import tkinter as tk from tkinter import ttk import random import math  class AfricaGeographyQuiz:     def __init__(self):         self.root = tk.Tk()         self.root.title(\"African Geography Quiz\")         self.root.geometry(\"800x700\")         self.root.configure(bg='lightblue')                  # African countries and their approximate center coordinates (normalized 0-1)         self.countries = {             'Algeria': (0.5, 0.25),             'Angola': (0.35, 0.75),             'Benin': (0.45, 0.55),             'Botswana': (0.55, 0.8),             'Burkina Faso': (0.4, 0.5),             'Burundi': (0.65, 0.7),             'Cameroon': (0.5, 0.6),             'Cape Verde': (0.15, 0.45),             'Central African Republic': (0.55, 0.6),             'Chad': (0.55, 0.5),             'Comoros': (0.8, 0.75),             'Democratic Republic of the Congo': (0.55, 0.7),             'Republic of the Congo': (0.5, 0.65),             'Djibouti': (0.8, 0.5),             'Egypt': (0.65, 0.25),             'Equatorial Guinea': (0.48, 0.6),             'Eritrea': (0.75, 0.45),             'Eswatini': (0.65, 0.85),             'Ethiopia': (0.75, 0.55),             'Gabon': (0.48, 0.65),             'Gambia': (0.25, 0.5),             'Ghana': (0.4, 0.55),             'Guinea': (0.3, 0.5),             'Guinea-Bissau': (0.25, 0.5),             'Ivory Coast': (0.35, 0.55),             'Kenya': (0.75, 0.65),             'Lesotho': (0.6, 0.85),             'Liberia': (0.3, 0.55),             'Libya': (0.55, 0.3),             'Madagascar': (0.85, 0.8),             'Malawi': (0.7, 0.75),             'Mali': (0.4, 0.45),             'Mauritania': (0.3, 0.4),             'Mauritius': (0.9, 0.8),             'Morocco': (0.35, 0.2),             'Mozambique': (0.7, 0.8),             'Namibia': (0.5, 0.8),             'Niger': (0.5, 0.45),             'Nigeria': (0.5, 0.55),             'Rwanda': (0.65, 0.7),             'Sao Tome and Principe': (0.45, 0.6),             'Senegal': (0.25, 0.45),             'Seychelles': (0.9, 0.7),             'Sierra Leone': (0.28, 0.55),             'Somalia': (0.85, 0.6),             'South Africa': (0.6, 0.85),             'South Sudan': (0.65, 0.55),             'Sudan': (0.65, 0.45),             'Tanzania': (0.7, 0.7),             'Togo': (0.43, 0.55),             'Tunisia': (0.5, 0.2),             'Uganda': (0.7, 0.65),             'Zambia': (0.6, 0.75),             'Zimbabwe': (0.65, 0.8)         }                  self.current_country = None         self.showing_answer = False                  self.setup_ui()         self.next_question()              def setup_ui(self):         # Title         title_label = tk.Label(self.root, text=\"African Geography Quiz\",                                font=('Arial', 20, 'bold'), bg='lightblue', fg='darkblue')         title_label.pack(pady=10)                  # Instructions         instructions = tk.Label(self.root,                                 text=\"Click on the map where you think the country is located!\",                                 font=('Arial', 12), bg='lightblue', fg='darkgreen')         instructions.pack(pady=5)                  # Country prompt         self.country_label = tk.Label(self.root, text=\"\",                                       font=('Arial', 16, 'bold'), bg='lightblue', fg='red')         self.country_label.pack(pady=10)                  # Canvas for the map         self.canvas = tk.Canvas(self.root, width=600, height=500, bg='lightcyan')         self.canvas.pack(pady=10)         self.canvas.bind(\"&lt;Button-1&gt;\", self.on_map_click)                  # Draw Africa outline         self.draw_africa_outline()              def draw_africa_outline(self):         \"\"\"Draw a simplified outline of Africa\"\"\"         # Simplified Africa outline coordinates (scaled to canvas)         africa_points = [             # Starting from northwest, going clockwise             100, 150,  # Morocco area             120, 140,  # North Morocco             140, 130,  # Algeria             200, 120,  # Tunisia             250, 130,  # Libya             350, 140,  # Egypt             370, 160,  # Egypt east             380, 200,  # Red Sea             390, 250,  # Sudan\/Eritrea             400, 300,  # Ethiopia             420, 350,  # Somalia             430, 400,  # Kenya             440, 450,  # Tanzania             450, 480,  # Mozambique north             460, 500,  # Mozambique             450, 520,  # South Africa east             400, 530,  # South Africa             350, 525,  # Lesotho area             300, 520,  # South Africa west             250, 510,  # Namibia             200, 500,  # Angola             150, 480,  # Angola north             120, 450,  # DRC             100, 400,  # Cameroon             90, 350,   # Nigeria             85, 300,   # Niger             80, 250,   # Mali             75, 200,   # Mauritania             80, 180,   # Morocco west             100, 150   # Back to start         ]                  self.canvas.create_polygon(africa_points, outline='black', fill='tan', width=2)              def next_question(self):         \"\"\"Select a random country for the next question\"\"\"         if self.showing_answer:             return                      self.current_country = random.choice(list(self.countries.keys()))         self.country_label.config(text=f\"Find: {self.current_country}\")                  # Clear any previous answer markers         self.canvas.delete(\"answer_marker\")              def on_map_click(self, event):         \"\"\"Handle clicks on the map\"\"\"         if self.showing_answer:             return                      # Show where the user clicked         self.canvas.create_oval(event.x-5, event.y-5, event.x+5, event.y+5,                                 fill='blue', outline='darkblue', width=2, tags=\"click_marker\")                  # Show the correct location         self.show_answer()              def show_answer(self):         \"\"\"Show the correct location of the country\"\"\"         self.showing_answer = True                  # Get the country's coordinates and convert to canvas coordinates         country_coords =<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-474850","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/474850","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=474850"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/474850\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=474850"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=474850"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=474850"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}