{"id":347,"date":"2025-09-10T02:48:18","date_gmt":"2025-09-10T02:48:18","guid":{"rendered":"https:\/\/iotnoob.com\/wordpress\/?p=347"},"modified":"2025-09-10T02:48:19","modified_gmt":"2025-09-10T02:48:19","slug":"%e0%b9%80%e0%b8%9e%e0%b8%b4%e0%b9%88%e0%b8%a1%e0%b8%81%e0%b8%b2%e0%b8%a3%e0%b9%80%e0%b8%81%e0%b9%87%e0%b8%9a%e0%b8%95%e0%b8%b3%e0%b9%81%e0%b8%ab%e0%b8%99%e0%b9%88%e0%b8%87%e0%b8%81%e0%b8%a5%e0%b9%89","status":"publish","type":"post","link":"https:\/\/iotnoob.com\/wordpress\/2025\/09\/10\/%e0%b9%80%e0%b8%9e%e0%b8%b4%e0%b9%88%e0%b8%a1%e0%b8%81%e0%b8%b2%e0%b8%a3%e0%b9%80%e0%b8%81%e0%b9%87%e0%b8%9a%e0%b8%95%e0%b8%b3%e0%b9%81%e0%b8%ab%e0%b8%99%e0%b9%88%e0%b8%87%e0%b8%81%e0%b8%a5%e0%b9%89\/","title":{"rendered":"\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e01\u0e32\u0e23\u0e40\u0e01\u0e47\u0e1a\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e01\u0e25\u0e49\u0e2d\u0e07 \u0e41\u0e25\u0e30 Seed \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e43\u0e2b\u0e49\u0e01\u0e25\u0e31\u0e1a\u0e21\u0e32\u0e41\u0e2a\u0e14\u0e07\u0e1c\u0e25\u0e17\u0e35\u0e48\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e40\u0e14\u0e34\u0e21"},"content":{"rendered":"\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-iotnoob-iot-project wp-block-embed-iotnoob-iot-project\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"azajo4Ifjn\"><a href=\"https:\/\/iotnoob.com\/wordpress\/2025\/09\/08\/build-map-game-use-perlin-noise\/\">\u0e2a\u0e23\u0e49\u0e32\u0e07 MAP \u0e43\u0e19\u0e40\u0e01\u0e21\u0e2a\u0e4c\u0e14\u0e49\u0e27\u0e22 PERLIN NOISE<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"&#8220;\u0e2a\u0e23\u0e49\u0e32\u0e07 MAP \u0e43\u0e19\u0e40\u0e01\u0e21\u0e2a\u0e4c\u0e14\u0e49\u0e27\u0e22 PERLIN NOISE&#8221; &#8212; IOTNOOB - IOT Project\" src=\"https:\/\/iotnoob.com\/wordpress\/2025\/09\/08\/build-map-game-use-perlin-noise\/embed\/#?secret=qbHkhPsZoQ#?secret=azajo4Ifjn\" data-secret=\"azajo4Ifjn\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/*\n * Click nbfs:\/\/nbhost\/SystemFileSystem\/Templates\/Licenses\/license-default.txt to change this license\n * Click nbfs:\/\/nbhost\/SystemFileSystem\/Templates\/Classes\/Class.java to edit this template\n *\/\npackage com.umgutils.game;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Random;\n\n\/**\n *\n * @author Thada\n * \u0e04\u0e25\u0e32\u0e2a\u0e2b\u0e25\u0e31\u0e01\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e40\u0e01\u0e21\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e41\u0e1c\u0e19\u0e17\u0e35\u0e48\n * \u0e2a\u0e23\u0e49\u0e32\u0e07 JFrame \u0e41\u0e25\u0e30\u0e40\u0e1e\u0e34\u0e48\u0e21 GamePanel \u0e40\u0e02\u0e49\u0e32\u0e44\u0e1b\n * \u0e04\u0e25\u0e32\u0e2a\u0e2b\u0e25\u0e31\u0e01\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e40\u0e01\u0e21\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e41\u0e1c\u0e19\u0e17\u0e35\u0e48\n * \u0e2a\u0e23\u0e49\u0e32\u0e07 JFrame \u0e41\u0e25\u0e30\u0e40\u0e1e\u0e34\u0e48\u0e21 GamePanel \u0e40\u0e02\u0e49\u0e32\u0e44\u0e1b\n * \u0e04\u0e25\u0e32\u0e2a\u0e2b\u0e25\u0e31\u0e01\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e40\u0e01\u0e21\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e41\u0e1c\u0e19\u0e17\u0e35\u0e48\n * \u0e2a\u0e23\u0e49\u0e32\u0e07 JFrame \u0e41\u0e25\u0e30\u0e40\u0e1e\u0e34\u0e48\u0e21 GamePanel \u0e40\u0e02\u0e49\u0e32\u0e44\u0e1b\n *\/\npublic class MapGame {\n\n    public static void main(String[] args) {\n        \/\/ \u0e2a\u0e23\u0e49\u0e32\u0e07 Frame \u0e2b\u0e25\u0e31\u0e01\u0e02\u0e2d\u0e07\u0e42\u0e1b\u0e23\u0e41\u0e01\u0e23\u0e21\n        JFrame frame = new JFrame(\"Map Based Game - \u0e43\u0e0a\u0e49\u0e1b\u0e38\u0e48\u0e21\u0e25\u0e39\u0e01\u0e28\u0e23\u0e2b\u0e23\u0e37\u0e2d\u0e25\u0e32\u0e01\u0e40\u0e21\u0e32\u0e2a\u0e4c\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e40\u0e04\u0e25\u0e37\u0e48\u0e2d\u0e19\u0e17\u0e35\u0e48\");\n        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n        frame.setSize(800, 800);\n        frame.setLocationRelativeTo(null); \/\/ \u0e41\u0e2a\u0e14\u0e07\u0e2b\u0e19\u0e49\u0e32\u0e15\u0e48\u0e32\u0e07\u0e01\u0e25\u0e32\u0e07\u0e08\u0e2d\n\n        \/\/ \u0e2a\u0e23\u0e49\u0e32\u0e07\u0e41\u0e25\u0e30\u0e40\u0e1e\u0e34\u0e48\u0e21 GamePanel \u0e17\u0e35\u0e48\u0e40\u0e1b\u0e47\u0e19\u0e2b\u0e31\u0e27\u0e43\u0e08\u0e2b\u0e25\u0e31\u0e01\u0e02\u0e2d\u0e07\u0e40\u0e01\u0e21\n        GamePanel gamePanel = new GamePanel();\n        frame.add(gamePanel);\n\n        \/\/ \u0e40\u0e1e\u0e34\u0e48\u0e21 listener \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e14\u0e31\u0e01\u0e08\u0e31\u0e1a event \u0e15\u0e2d\u0e19\u0e1b\u0e34\u0e14\u0e2b\u0e19\u0e49\u0e32\u0e15\u0e48\u0e32\u0e07 \u0e41\u0e25\u0e30\u0e17\u0e33\u0e01\u0e32\u0e23\u0e40\u0e0b\u0e1f\u0e40\u0e01\u0e21\n        frame.addWindowListener(new java.awt.event.WindowAdapter() {\n            @Override\n            public void windowClosing(java.awt.event.WindowEvent windowEvent) {\n                gamePanel.saveGame();\n            }\n        });\n\n        frame.setVisible(true);\n    }\n\n    \/**\n     * GamePanel \u0e17\u0e33\u0e2b\u0e19\u0e49\u0e32\u0e17\u0e35\u0e48\u0e27\u0e32\u0e14\u0e41\u0e1c\u0e19\u0e17\u0e35\u0e48\u0e41\u0e25\u0e30\u0e08\u0e31\u0e14\u0e01\u0e32\u0e23\u0e01\u0e32\u0e23\u0e04\u0e27\u0e1a\u0e04\u0e38\u0e21\n     *\/\n    static class GamePanel extends JPanel {\n\n        \/\/ --- \u0e40\u0e1e\u0e34\u0e48\u0e21\u0e04\u0e48\u0e32\u0e04\u0e07\u0e17\u0e35\u0e48\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e44\u0e1f\u0e25\u0e4c\u0e40\u0e0b\u0e1f ---\n        private static final String SAVE_FILE = \"save.dat\";\n\n        \/\/ --- \u0e04\u0e48\u0e32\u0e04\u0e07\u0e17\u0e35\u0e48\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e1b\u0e23\u0e31\u0e1a\u0e41\u0e15\u0e48\u0e07\u0e40\u0e01\u0e21 ---\n        private static final int TILE_SIZE = 16; \/\/ \u0e02\u0e19\u0e32\u0e14\u0e02\u0e2d\u0e07\u0e41\u0e15\u0e48\u0e25\u0e30\u0e44\u0e17\u0e25\u0e4c (pixel)\n        private static final int CHUNK_WIDTH = 32; \/\/ \u0e08\u0e33\u0e19\u0e27\u0e19\u0e44\u0e17\u0e25\u0e4c\u0e43\u0e19\u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19\u0e02\u0e2d\u0e07 1 chunk\n        private static final int CHUNK_HEIGHT = 32; \/\/ \u0e08\u0e33\u0e19\u0e27\u0e19\u0e44\u0e17\u0e25\u0e4c\u0e43\u0e19\u0e41\u0e19\u0e27\u0e15\u0e31\u0e49\u0e07\u0e02\u0e2d\u0e07 1 chunk\n        private static final int MOVE_SPEED = 10; \/\/ \u0e04\u0e27\u0e32\u0e21\u0e40\u0e23\u0e47\u0e27\u0e43\u0e19\u0e01\u0e32\u0e23\u0e40\u0e04\u0e25\u0e37\u0e48\u0e2d\u0e19\u0e17\u0e35\u0e48 (pixel)\n        private static final double NOISE_SCALE = 0.05; \/\/ \u0e2a\u0e40\u0e01\u0e25\u0e02\u0e2d\u0e07 Perlin noise (\u0e04\u0e48\u0e32\u0e22\u0e34\u0e48\u0e07\u0e19\u0e49\u0e2d\u0e22 \u0e41\u0e1c\u0e19\u0e17\u0e35\u0e48\u0e22\u0e34\u0e48\u0e07\u0e40\u0e23\u0e35\u0e22\u0e1a)\n        private static final double RESOURCE_NOISE_SCALE = 0.2; \/\/ \u0e2a\u0e40\u0e01\u0e25\u0e02\u0e2d\u0e07 noise \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e17\u0e23\u0e31\u0e1e\u0e22\u0e32\u0e01\u0e23\n        private static final double RESOURCE_THRESHOLD = 0.7; \/\/ \u0e04\u0e48\u0e32 noise \u0e17\u0e35\u0e48\u0e08\u0e30\u0e40\u0e23\u0e34\u0e48\u0e21\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e17\u0e23\u0e31\u0e1e\u0e22\u0e32\u0e01\u0e23 (0.0 - 1.0)\n\n        \/\/ --- \u0e15\u0e31\u0e27\u0e41\u0e1b\u0e23\u0e2a\u0e16\u0e32\u0e19\u0e30\u0e02\u0e2d\u0e07\u0e40\u0e01\u0e21 ---\n        private int cameraX = 0; \/\/ \u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e01\u0e25\u0e49\u0e2d\u0e07\u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19 (World Coordinate)\n        private int cameraY = 0; \/\/ \u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e01\u0e25\u0e49\u0e2d\u0e07\u0e41\u0e19\u0e27\u0e15\u0e31\u0e49\u0e07 (World Coordinate)\n\n        \/\/ --- \u0e15\u0e31\u0e27\u0e41\u0e1b\u0e23\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a Seed \u0e02\u0e2d\u0e07\u0e42\u0e25\u0e01 ---\n        private int worldSeed;\n        private int resourceSeed;\n        \n        \/\/ --- \u0e15\u0e31\u0e27\u0e41\u0e1b\u0e23\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e08\u0e31\u0e14\u0e01\u0e32\u0e23\u0e01\u0e32\u0e23\u0e25\u0e32\u0e01\u0e40\u0e21\u0e32\u0e2a\u0e4c ---\n        private Point mouseDragStart;\n        private Point cameraStart;\n\n\n        \/\/ Cache \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e40\u0e01\u0e47\u0e1a Chunk \u0e17\u0e35\u0e48\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e41\u0e25\u0e49\u0e27\n        private final Map&lt;Point, int[][]> chunkCache = new HashMap&lt;>();\n        \/\/ \u0e1b\u0e23\u0e30\u0e01\u0e32\u0e28\u0e15\u0e31\u0e27\u0e41\u0e1b\u0e23\u0e44\u0e27\u0e49\u0e01\u0e48\u0e2d\u0e19 \u0e41\u0e15\u0e48\u0e22\u0e31\u0e07\u0e44\u0e21\u0e48\u0e2a\u0e23\u0e49\u0e32\u0e07 object\n        private PerlinNoise noiseGenerator;\n        private PerlinNoise resourceNoiseGenerator;\n\n        public GamePanel() {\n            this.setFocusable(true); \/\/ \u0e17\u0e33\u0e43\u0e2b\u0e49 Panel \u0e19\u0e35\u0e49\u0e23\u0e31\u0e1a input \u0e08\u0e32\u0e01 keyboard \u0e44\u0e14\u0e49\n            this.setBackground(Color.BLACK);\n\n            \/\/ \u0e42\u0e2b\u0e25\u0e14\u0e40\u0e01\u0e21 (\u0e0b\u0e36\u0e48\u0e07\u0e08\u0e30\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32 seed \u0e41\u0e25\u0e30\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07)\n            loadGame();\n\n            \/\/ *** \u0e2a\u0e23\u0e49\u0e32\u0e07 Noise Generators \u0e42\u0e14\u0e22\u0e43\u0e0a\u0e49 Seed \u0e17\u0e35\u0e48\u0e42\u0e2b\u0e25\u0e14\u0e21\u0e32 ***\n            this.noiseGenerator = new PerlinNoise(worldSeed);\n            this.resourceNoiseGenerator = new PerlinNoise(resourceSeed);\n\n            \/\/ \u0e40\u0e1e\u0e34\u0e48\u0e21 KeyListener \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e23\u0e31\u0e1a\u0e01\u0e32\u0e23\u0e01\u0e14\u0e1b\u0e38\u0e48\u0e21\n            this.addKeyListener(new KeyAdapter() {\n                @Override\n                public void keyPressed(KeyEvent e) {\n                    handleKeyPress(e.getKeyCode());\n                }\n            });\n            \n            \/\/ --- \u0e40\u0e1e\u0e34\u0e48\u0e21 Mouse Listener \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e01\u0e32\u0e23\u0e25\u0e32\u0e01\u0e41\u0e1c\u0e19\u0e17\u0e35\u0e48 ---\n            MouseAdapter mouseAdapter = new MouseAdapter() {\n                @Override\n                public void mousePressed(MouseEvent e) {\n                    \/\/ \u0e40\u0e21\u0e37\u0e48\u0e2d\u0e01\u0e14\u0e40\u0e21\u0e32\u0e2a\u0e4c, \u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01\u0e08\u0e38\u0e14\u0e40\u0e23\u0e34\u0e48\u0e21\u0e15\u0e49\u0e19\u0e02\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e25\u0e32\u0e01 \u0e41\u0e25\u0e30\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e02\u0e2d\u0e07\u0e01\u0e25\u0e49\u0e2d\u0e07 \u0e13 \u0e15\u0e2d\u0e19\u0e19\u0e31\u0e49\u0e19\n                    mouseDragStart = e.getPoint();\n                    cameraStart = new Point(cameraX, cameraY);\n                    setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));\n                }\n\n                @Override\n                public void mouseDragged(MouseEvent e) {\n                    if (mouseDragStart != null) {\n                        \/\/ \u0e04\u0e33\u0e19\u0e27\u0e13\u0e23\u0e30\u0e22\u0e30\u0e17\u0e32\u0e07\u0e17\u0e35\u0e48\u0e40\u0e21\u0e32\u0e2a\u0e4c\u0e25\u0e32\u0e01\u0e44\u0e1b\n                        int dx = e.getX() - mouseDragStart.x;\n                        int dy = e.getY() - mouseDragStart.y;\n                        \n                        \/\/ \u0e2d\u0e31\u0e1b\u0e40\u0e14\u0e15\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e01\u0e25\u0e49\u0e2d\u0e07\u0e15\u0e32\u0e21\u0e23\u0e30\u0e22\u0e30\u0e17\u0e35\u0e48\u0e25\u0e32\u0e01 (\u0e25\u0e32\u0e01\u0e02\u0e27\u0e32\u0e01\u0e25\u0e49\u0e2d\u0e07\u0e44\u0e1b\u0e0b\u0e49\u0e32\u0e22, \u0e25\u0e32\u0e01\u0e0b\u0e49\u0e32\u0e22\u0e01\u0e25\u0e49\u0e2d\u0e07\u0e44\u0e1b\u0e02\u0e27\u0e32)\n                        cameraX = cameraStart.x - dx;\n                        cameraY = cameraStart.y - dy;\n                        \n                        \/\/ \u0e27\u0e32\u0e14\u0e41\u0e1c\u0e19\u0e17\u0e35\u0e48\u0e43\u0e2b\u0e21\u0e48\n                        repaint();\n                    }\n                }\n\n                @Override\n                public void mouseReleased(MouseEvent e) {\n                    \/\/ \u0e40\u0e21\u0e37\u0e48\u0e2d\u0e1b\u0e25\u0e48\u0e2d\u0e22\u0e40\u0e21\u0e32\u0e2a\u0e4c, \u0e23\u0e35\u0e40\u0e0b\u0e47\u0e15\u0e04\u0e48\u0e32\u0e01\u0e32\u0e23\u0e25\u0e32\u0e01\n                    mouseDragStart = null;\n                    cameraStart = null;\n                    setCursor(Cursor.getDefaultCursor());\n                }\n            };\n            \n            addMouseListener(mouseAdapter);\n            addMouseMotionListener(mouseAdapter);\n        }\n\n        \/**\n         * \u0e08\u0e31\u0e14\u0e01\u0e32\u0e23\u0e01\u0e32\u0e23\u0e01\u0e14\u0e1b\u0e38\u0e48\u0e21\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e40\u0e04\u0e25\u0e37\u0e48\u0e2d\u0e19\u0e17\u0e35\u0e48\n         * @param keyCode \u0e23\u0e2b\u0e31\u0e2a\u0e02\u0e2d\u0e07\u0e1b\u0e38\u0e48\u0e21\u0e17\u0e35\u0e48\u0e16\u0e39\u0e01\u0e01\u0e14\n         *\/\n        private void handleKeyPress(int keyCode) {\n            switch (keyCode) {\n                case KeyEvent.VK_UP:\n                    cameraY -= MOVE_SPEED;\n                    break;\n                case KeyEvent.VK_DOWN:\n                    cameraY += MOVE_SPEED;\n                    break;\n                case KeyEvent.VK_LEFT:\n                    cameraX -= MOVE_SPEED;\n                    break;\n                case KeyEvent.VK_RIGHT:\n                    cameraX += MOVE_SPEED;\n                    break;\n            }\n            repaint(); \/\/ \u0e2a\u0e31\u0e48\u0e07\u0e43\u0e2b\u0e49\u0e27\u0e32\u0e14\u0e2b\u0e19\u0e49\u0e32\u0e08\u0e2d\u0e43\u0e2b\u0e21\u0e48\u0e2b\u0e25\u0e31\u0e07\u0e08\u0e32\u0e01\u0e40\u0e04\u0e25\u0e37\u0e48\u0e2d\u0e19\u0e17\u0e35\u0e48\n        }\n\n        \/**\n         * \u0e40\u0e21\u0e18\u0e2d\u0e14\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e40\u0e0b\u0e1f\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e41\u0e25\u0e30 Seed \u0e1b\u0e31\u0e08\u0e08\u0e38\u0e1a\u0e31\u0e19\u0e25\u0e07\u0e44\u0e1f\u0e25\u0e4c\n         *\/\n        public void saveGame() {\n            Properties properties = new Properties();\n            properties.setProperty(\"cameraX\", String.valueOf(cameraX));\n            properties.setProperty(\"cameraY\", String.valueOf(cameraY));\n            \/\/ --- \u0e40\u0e0b\u0e1f\u0e04\u0e48\u0e32 Seed \u0e25\u0e07\u0e44\u0e1b\u0e14\u0e49\u0e27\u0e22 ---\n            properties.setProperty(\"worldSeed\", String.valueOf(worldSeed));\n            properties.setProperty(\"resourceSeed\", String.valueOf(resourceSeed));\n\n            \/\/ \u0e43\u0e0a\u0e49 try-with-resources \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e43\u0e2b\u0e49\u0e41\u0e19\u0e48\u0e43\u0e08\u0e27\u0e48\u0e32 stream \u0e16\u0e39\u0e01\u0e1b\u0e34\u0e14\u0e40\u0e2a\u0e21\u0e2d\n            try (OutputStream output = new FileOutputStream(SAVE_FILE)) {\n                properties.store(output, \"Map Game Save Data\");\n                System.out.println(\"Game saved successfully!\");\n            } catch (IOException io) {\n                System.err.println(\"Error saving game state: \" + io.getMessage());\n            }\n        }\n\n        \/**\n         * \u0e40\u0e21\u0e18\u0e2d\u0e14\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e42\u0e2b\u0e25\u0e14\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e41\u0e25\u0e30 Seed \u0e08\u0e32\u0e01\u0e44\u0e1f\u0e25\u0e4c\n         *\/\n        private void loadGame() {\n            File saveFile = new File(SAVE_FILE);\n            \/\/ \u0e15\u0e23\u0e27\u0e08\u0e2a\u0e2d\u0e1a\u0e27\u0e48\u0e32\u0e21\u0e35\u0e44\u0e1f\u0e25\u0e4c\u0e40\u0e0b\u0e1f\u0e2d\u0e22\u0e39\u0e48\u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48\n            if (!saveFile.exists()) {\n                System.out.println(\"No save file found. Creating a new world.\");\n                \/\/ --- \u0e16\u0e49\u0e32\u0e44\u0e21\u0e48\u0e21\u0e35\u0e40\u0e0b\u0e1f \u0e43\u0e2b\u0e49\u0e2a\u0e38\u0e48\u0e21 Seed \u0e43\u0e2b\u0e21\u0e48\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e42\u0e25\u0e01\u0e43\u0e1a\u0e19\u0e35\u0e49 ---\n                Random rand = new Random();\n                this.worldSeed = rand.nextInt();\n                this.resourceSeed = rand.nextInt();\n                this.cameraX = 0;\n                this.cameraY = 0;\n                return; \/\/ \u0e08\u0e1a\u0e01\u0e32\u0e23\u0e17\u0e33\u0e07\u0e32\u0e19\n            }\n\n            Properties properties = new Properties();\n            \/\/ \u0e43\u0e0a\u0e49 try-with-resources \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e43\u0e2b\u0e49\u0e41\u0e19\u0e48\u0e43\u0e08\u0e27\u0e48\u0e32 stream \u0e16\u0e39\u0e01\u0e1b\u0e34\u0e14\u0e40\u0e2a\u0e21\u0e2d\n            try (InputStream input = new FileInputStream(SAVE_FILE)) {\n                properties.load(input);\n                \/\/ \u0e42\u0e2b\u0e25\u0e14\u0e04\u0e48\u0e32\u0e41\u0e25\u0e30\u0e41\u0e1b\u0e25\u0e07\u0e40\u0e1b\u0e47\u0e19 Integer\n                this.cameraX = Integer.parseInt(properties.getProperty(\"cameraX\", \"0\"));\n                this.cameraY = Integer.parseInt(properties.getProperty(\"cameraY\", \"0\"));\n                \/\/ --- \u0e42\u0e2b\u0e25\u0e14\u0e04\u0e48\u0e32 Seed \u0e01\u0e25\u0e31\u0e1a\u0e21\u0e32 ---\n                this.worldSeed = Integer.parseInt(properties.getProperty(\"worldSeed\"));\n                this.resourceSeed = Integer.parseInt(properties.getProperty(\"resourceSeed\"));\n                \n                System.out.println(\"Game loaded successfully! Starting at (\" + cameraX + \", \" + cameraY + \")\");\n            } catch (IOException | NumberFormatException ex) {\n                \/\/ \u0e08\u0e31\u0e14\u0e01\u0e32\u0e23\u0e01\u0e23\u0e13\u0e35\u0e44\u0e1f\u0e25\u0e4c\u0e40\u0e2a\u0e35\u0e22\u0e2b\u0e32\u0e22\u0e2b\u0e23\u0e37\u0e2d\u0e2d\u0e48\u0e32\u0e19\u0e04\u0e48\u0e32\u0e44\u0e21\u0e48\u0e44\u0e14\u0e49\n                System.err.println(\"Could not load save file: \" + ex.getMessage() + \". Creating a new world.\");\n                \/\/ --- \u0e16\u0e49\u0e32\u0e44\u0e1f\u0e25\u0e4c\u0e40\u0e0b\u0e1f\u0e40\u0e2a\u0e35\u0e22 \u0e01\u0e47\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e42\u0e25\u0e01\u0e43\u0e2b\u0e21\u0e48 ---\n                Random rand = new Random();\n                this.worldSeed = rand.nextInt();\n                this.resourceSeed = rand.nextInt();\n                this.cameraX = 0;\n                this.cameraY = 0;\n            }\n        }\n\n        \/**\n         * \u0e40\u0e21\u0e18\u0e2d\u0e14\u0e2b\u0e25\u0e31\u0e01\u0e17\u0e35\u0e48\u0e43\u0e0a\u0e49\u0e43\u0e19\u0e01\u0e32\u0e23\u0e27\u0e32\u0e14 Component \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14\n         *\/\n        @Override\n        protected void paintComponent(Graphics g) {\n            super.paintComponent(g);\n\n            \/\/ \u0e04\u0e33\u0e19\u0e27\u0e13\u0e02\u0e2d\u0e1a\u0e40\u0e02\u0e15\u0e02\u0e2d\u0e07 Chunk \u0e17\u0e35\u0e48\u0e15\u0e49\u0e2d\u0e07\u0e27\u0e32\u0e14\u0e1a\u0e19\u0e2b\u0e19\u0e49\u0e32\u0e08\u0e2d\n            int startChunkX = (int) Math.floor((double) cameraX \/ (CHUNK_WIDTH * TILE_SIZE));\n            int startChunkY = (int) Math.floor((double) cameraY \/ (CHUNK_HEIGHT * TILE_SIZE));\n            int endChunkX = (int) Math.floor((double) (cameraX + getWidth()) \/ (CHUNK_WIDTH * TILE_SIZE));\n            int endChunkY = (int) Math.floor((double) (cameraY + getHeight()) \/ (CHUNK_HEIGHT * TILE_SIZE));\n            \n            \/\/ \u0e27\u0e19\u0e25\u0e39\u0e1b\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e27\u0e32\u0e14\u0e17\u0e38\u0e01 Chunk \u0e17\u0e35\u0e48\u0e21\u0e2d\u0e07\u0e40\u0e2b\u0e47\u0e19\n            for (int cy = startChunkY; cy &lt;= endChunkY; cy++) {\n                for (int cx = startChunkX; cx &lt;= endChunkX; cx++) {\n                    drawChunk(g, cx, cy);\n                }\n            }\n             \/\/ \u0e27\u0e32\u0e14\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e1b\u0e31\u0e08\u0e08\u0e38\u0e1a\u0e31\u0e19\u0e02\u0e2d\u0e07\u0e01\u0e25\u0e49\u0e2d\u0e07\u0e41\u0e25\u0e30 Seed \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e01\u0e32\u0e23\u0e14\u0e35\u0e1a\u0e31\u0e01\n            g.setColor(Color.WHITE);\n            g.drawString(String.format(\"Position: (%d, %d)\", cameraX, cameraY), 10, 20);\n            g.drawString(String.format(\"World Seed: %d\", worldSeed), 10, 40);\n        }\n        \n        \/**\n         * \u0e27\u0e32\u0e14 Chunk 1 \u0e2d\u0e31\u0e19\u0e17\u0e35\u0e48\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07 (chunkX, chunkY)\n         * @param g Graphics object\n         * @param chunkX \u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07 Chunk \u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19\n         * @param chunkY \u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07 Chunk \u0e41\u0e19\u0e27\u0e15\u0e31\u0e49\u0e07\n         *\/\n        private void drawChunk(Graphics g, int chunkX, int chunkY) {\n            Point chunkPos = new Point(chunkX, chunkY);\n            int[][] tiles;\n\n            \/\/ \u0e15\u0e23\u0e27\u0e08\u0e2a\u0e2d\u0e1a\u0e27\u0e48\u0e32 Chunk \u0e19\u0e35\u0e49\u0e2d\u0e22\u0e39\u0e48\u0e43\u0e19 cache \u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e21\u0e48\n            if (chunkCache.containsKey(chunkPos)) {\n                tiles = chunkCache.get(chunkPos); \/\/ \u0e16\u0e49\u0e32\u0e21\u0e35\u0e2d\u0e22\u0e39\u0e48\u0e41\u0e25\u0e49\u0e27 \u0e01\u0e47\u0e14\u0e36\u0e07\u0e08\u0e32\u0e01 cache\n            } else {\n                tiles = generateChunk(chunkX, chunkY); \/\/ \u0e16\u0e49\u0e32\u0e22\u0e31\u0e07\u0e44\u0e21\u0e48\u0e21\u0e35 \u0e01\u0e47\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e43\u0e2b\u0e21\u0e48\n                chunkCache.put(chunkPos, tiles); \/\/ \u0e41\u0e25\u0e49\u0e27\u0e40\u0e01\u0e47\u0e1a\u0e25\u0e07 cache\n            }\n\n            \/\/ \u0e27\u0e19\u0e25\u0e39\u0e1b\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e27\u0e32\u0e14\u0e17\u0e38\u0e01\u0e44\u0e17\u0e25\u0e4c\u0e43\u0e19 Chunk\n            for (int y = 0; y &lt; CHUNK_HEIGHT; y++) {\n                for (int x = 0; x &lt; CHUNK_WIDTH; x++) {\n                    int tileType = tiles[y][x];\n                    Color tileColor = getTileColor(tileType);\n\n                    \/\/ \u0e04\u0e33\u0e19\u0e27\u0e13\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e17\u0e35\u0e48\u0e08\u0e30\u0e27\u0e32\u0e14\u0e1a\u0e19\u0e2b\u0e19\u0e49\u0e32\u0e08\u0e2d\n                    int drawX = chunkX * CHUNK_WIDTH * TILE_SIZE + x * TILE_SIZE - cameraX;\n                    int drawY = chunkY * CHUNK_HEIGHT * TILE_SIZE + y * TILE_SIZE - cameraY;\n\n                    g.setColor(tileColor);\n                    g.fillRect(drawX, drawY, TILE_SIZE, TILE_SIZE);\n                }\n            }\n        }\n        \n        \/**\n         * \u0e2a\u0e23\u0e49\u0e32\u0e07\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e44\u0e17\u0e25\u0e4c\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a Chunk \u0e17\u0e35\u0e48\u0e01\u0e33\u0e2b\u0e19\u0e14\u0e42\u0e14\u0e22\u0e43\u0e0a\u0e49 Perlin Noise\n         * @param chunkX \u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07 Chunk \u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19\n         * @param chunkY \u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07 Chunk \u0e41\u0e19\u0e27\u0e15\u0e31\u0e49\u0e07\n         * @return 2D array \u0e02\u0e2d\u0e07 tile types\n         *\/\n        private int[][] generateChunk(int chunkX, int chunkY) {\n            int[][] tiles = new int[CHUNK_HEIGHT][CHUNK_WIDTH];\n            for (int y = 0; y &lt; CHUNK_HEIGHT; y++) {\n                for (int x = 0; x &lt; CHUNK_WIDTH; x++) {\n                    \/\/ \u0e04\u0e33\u0e19\u0e27\u0e13\u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e44\u0e17\u0e25\u0e4c\u0e43\u0e19\u0e42\u0e25\u0e01\u0e08\u0e23\u0e34\u0e07 (World Coordinate)\n                    int worldX = chunkX * CHUNK_WIDTH + x;\n                    int worldY = chunkY * CHUNK_HEIGHT + y;\n\n                    \/\/ \u0e2a\u0e23\u0e49\u0e32\u0e07\u0e04\u0e48\u0e32 noise \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e20\u0e39\u0e21\u0e34\u0e1b\u0e23\u0e30\u0e40\u0e17\u0e28\n                    double elevation = noiseGenerator.eval(worldX * NOISE_SCALE, worldY * NOISE_SCALE);\n                    \n                    \/\/ \u0e01\u0e33\u0e2b\u0e19\u0e14\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\u0e44\u0e17\u0e25\u0e4c\u0e15\u0e32\u0e21\u0e04\u0e48\u0e32 noise\n                    int tileType;\n                    if (elevation &lt; -0.4) tileType = 0; \/\/ Deep Water\n                    else if (elevation &lt; -0.2) tileType = 1; \/\/ Shallow Water\n                    else if (elevation &lt; -0.1) tileType = 2; \/\/ Sand\n                    else if (elevation &lt; 0.5) tileType = 3; \/\/ Grass\n                    else if (elevation &lt; 0.8) tileType = 4; \/\/ Rock\n                    else tileType = 5; \/\/ Snow\n\n                    \/\/ \u0e16\u0e49\u0e32\u0e40\u0e1b\u0e47\u0e19\u0e1e\u0e37\u0e49\u0e19\u0e2b\u0e0d\u0e49\u0e32 (Grass) \u0e43\u0e2b\u0e49\u0e25\u0e2d\u0e07\u0e2a\u0e38\u0e48\u0e21\u0e17\u0e23\u0e31\u0e1e\u0e22\u0e32\u0e01\u0e23\n                    if (tileType == 3) {\n                        double resourceValue = resourceNoiseGenerator.eval(worldX * RESOURCE_NOISE_SCALE, worldY * RESOURCE_NOISE_SCALE);\n                        \/\/ resourceValue \u0e08\u0e30\u0e21\u0e35\u0e04\u0e48\u0e32\u0e23\u0e30\u0e2b\u0e27\u0e48\u0e32\u0e07 -1 \u0e16\u0e36\u0e07 1 \u0e40\u0e23\u0e32\u0e08\u0e36\u0e07\u0e15\u0e49\u0e2d\u0e07\u0e1b\u0e23\u0e31\u0e1a\u0e43\u0e2b\u0e49\u0e2d\u0e22\u0e39\u0e48\u0e43\u0e19\u0e0a\u0e48\u0e27\u0e07 0-1\n                        if ((resourceValue + 1) \/ 2 > RESOURCE_THRESHOLD) {\n                           tileType = 6; \/\/ Resource\n                        }\n                    }\n\n                    tiles[y][x] = tileType;\n                }\n            }\n            return tiles;\n        }\n\n        \/**\n         * \u0e04\u0e37\u0e19\u0e04\u0e48\u0e32\u0e2a\u0e35\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e44\u0e17\u0e25\u0e4c\u0e41\u0e15\u0e48\u0e25\u0e30\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\n         * @param tileType \u0e23\u0e2b\u0e31\u0e2a\u0e02\u0e2d\u0e07\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\u0e44\u0e17\u0e25\u0e4c\n         * @return Color object\n         *\/\n        private Color getTileColor(int tileType) {\n            switch (tileType) {\n                case 0: return new Color(0, 0, 128); \/\/ Deep Water\n                case 1: return new Color(0, 100, 255); \/\/ Shallow Water\n                case 2: return new Color(240, 230, 140); \/\/ Sand\n                case 3: return new Color(34, 139, 34);  \/\/ Grass\n                case 4: return new Color(139, 137, 137); \/\/ Rock\n                case 5: return new Color(255, 250, 250); \/\/ Snow\n                case 6: return Color.RED; \/\/ Resource\n                default: return Color.BLACK;\n            }\n        }\n    }\n\n    \/**\n     * Perlin Noise Generator\n     * \u0e19\u0e35\u0e48\u0e04\u0e37\u0e2d\u0e01\u0e32\u0e23 \u03c5\u03bb\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u0e02\u0e2d\u0e07 \"Improved Perlin Noise\" \u0e42\u0e14\u0e22 Ken Perlin\n     * \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e43\u0e2b\u0e49\u0e44\u0e1f\u0e25\u0e4c\u0e19\u0e35\u0e49\u0e17\u0e33\u0e07\u0e32\u0e19\u0e44\u0e14\u0e49\u0e14\u0e49\u0e27\u0e22\u0e15\u0e31\u0e27\u0e40\u0e2d\u0e07\u0e42\u0e14\u0e22\u0e44\u0e21\u0e48\u0e15\u0e49\u0e2d\u0e07\u0e1e\u0e36\u0e48\u0e07 library \u0e20\u0e32\u0e22\u0e19\u0e2d\u0e01\n     *\/\n     static class PerlinNoise {\n        private final int[] p = new int[512];\n        \n        public PerlinNoise(int seed) {\n            int[] permutation = new int[256];\n            for (int i = 0; i &lt; 256; i++) {\n                permutation[i] = i;\n            }\n            Random rand = new Random(seed);\n            for (int i = 255; i > 0; i--) {\n                int index = rand.nextInt(i + 1);\n                int temp = permutation[index];\n                permutation[index] = permutation[i];\n                permutation[i] = temp;\n            }\n            for (int i = 0; i &lt; 256; i++) {\n                p[i] = p[i + 256] = permutation[i];\n            }\n        }\n\n        public double eval(double x, double y) {\n            int X = (int) Math.floor(x) &amp; 255;\n            int Y = (int) Math.floor(y) &amp; 255;\n            x -= Math.floor(x);\n            y -= Math.floor(y);\n            double u = fade(x);\n            double v = fade(y);\n            int A = p[X] + Y;\n            int B = p[X + 1] + Y;\n            return lerp(v, lerp(u, grad(p[A], x, y), grad(p[B], x - 1, y)),\n                    lerp(u, grad(p[A + 1], x, y - 1), grad(p[B + 1], x - 1, y - 1)));\n        }\n\n        private static double fade(double t) {\n            return t * t * t * (t * (t * 6 - 15) + 10);\n        }\n\n        private static double lerp(double t, double a, double b) {\n            return a + t * (b - a);\n        }\n\n        private static double grad(int hash, double x, double y) {\n            int h = hash &amp; 15;\n            double u = h &lt; 8 ? x : y;\n            double v = h &lt; 4 ? y : h == 12 || h == 14 ? x : 0;\n            return ((h &amp; 1) == 0 ? u : -u) + ((h &amp; 2) == 0 ? v : -v);\n        }\n    }\n}\n\n<\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"324\" height=\"150\" src=\"https:\/\/iotnoob.com\/wordpress\/wp-content\/uploads\/2025\/09\/image-2.png\" alt=\"\" class=\"wp-image-348\" srcset=\"https:\/\/iotnoob.com\/wordpress\/wp-content\/uploads\/2025\/09\/image-2.png 324w, https:\/\/iotnoob.com\/wordpress\/wp-content\/uploads\/2025\/09\/image-2-300x139.png 300w\" sizes=\"auto, (max-width: 324px) 100vw, 324px\" \/><\/figure>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":339,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,4,2],"tags":[],"class_list":["post-347","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-game","category-java","category-programming"],"_links":{"self":[{"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/posts\/347","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/comments?post=347"}],"version-history":[{"count":1,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/posts\/347\/revisions"}],"predecessor-version":[{"id":349,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/posts\/347\/revisions\/349"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/media\/339"}],"wp:attachment":[{"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/media?parent=347"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/categories?post=347"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iotnoob.com\/wordpress\/wp-json\/wp\/v2\/tags?post=347"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}