{"repo": "bysghjx/translex", "files": {"tsp/tests/TspPlaceholderTest.java": {"dc07548d2d6026c0116e9ae07bbc97eb98d0d22e": {"t": 1785746139.943277, "n": 9438, "h": "645a21424102fe68", "c": "package tsp.tests;\n\nimport tsp.*;\nimport java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * TSP + number placeholder ({0}{1}) protection test.\n * Verifies AI preserves {N} placeholders and fillNumbers restores correct numbers.\n * Run: java tsp.tests.TspPlaceholderTest \n */\npublic final class TspPlaceholderTest {\n\n private static final Style GRAY = Style.of(\"#AAAAAA\");\n private static final Style GREEN = Style.of(\"#55FF55\");\n private static final Style AQUA = Style.of(\"#55FFFF\");\n\n // Tusk Luck paragraph (4 lore lines). Numbers ALREADY replaced with {0}{1} placeholders.\n // {0} = \"+0.28\" (green), {1} = \"100\" (gray/default)\n private static final String[] VALS = {\"+0.28\", \"100\"};\n\n private static final List PARAGRAPH = List.of(\n new StyledSegment(\"Gain \", GRAY),\n new StyledSegment(\"{0} Magic Find \", GREEN), // {0} = +0.28\n new StyledSegment(\" for every\\n\", GRAY),\n new StyledSegment(\"{1} \", GRAY), // {1} = 100 (default color)\n new StyledSegment(\"Mining Fortune\", AQUA),\n new StyledSegment(\", doubled in the\\n\", GRAY),\n new StyledSegment(\"Glacite Tunnels \", GRAY),\n new StyledSegment(\"and \", GRAY),\n new StyledSegment(\"Glacite\", AQUA),\n new StyledSegment(\"\\nMineshafts\", GRAY),\n new StyledSegment(\".\", GRAY)\n );\n\n private static final String SYSTEM_PROMPT =\n \"You are a Minecraft item tooltip translator. Translate to Simplified Chinese. \" +\n \"[[NUMBER||TEXT]] tokens: NUMBER is a style ID PERMANENTLY BOUND to its content. \" +\n \" rule: [[N||X]] MUST become [[N||translated X]] - N stays with original content. \" +\n \"NEVER move content to a different NUMBER. NEVER merge or split tokens. \" +\n \"{0} {1} etc. are NUMBER placeholders - output them LITERALLY, never fill, remove, or translate them. \" +\n \"Reorder whole tokens freely. Output ONLY the translated text, no markdown.\";\n\n public static void main(String[] args) throws Exception {\n if (args.length < 1) {\n System.err.println(\"Usage: java tsp.tests.TspPlaceholderTest \");\n System.exit(1);\n }\n String apiKey = args[0];\n\n System.out.println(\"=== TSP + Number Placeholder Test ===\\n\");\n System.out.println(\"vals: {0}=\\\"\" + VALS[0] + \"\\\", {1}=\\\"\" + VALS[1] + \"\\\"\\n\");\n\n // Encode\n TspRegistry registry = new TspRegistry();\n TspEncoder encoder = TspEncoder.withAutoDefault(registry, PARAGRAPH);\n String encoded = encoder.encode(PARAGRAPH);\n\n System.out.println(\"── TSP Encoded (numbers -> {N} placeholders) ──\");\n System.out.println(encoded);\n System.out.println(\"\\n── Registry ──\");\n for (int i = 0; i < registry.size(); i++) {\n System.out.printf(\" ID %d -> %s%n\", i, colorName(registry.getStyle(i)));\n }\n System.out.println(\" default -> \" + colorName(encoder.defaultStyle()) + \"\\n\");\n\n // Send to AI\n String body = openAiBody(\"deepseek-v4-flash\", SYSTEM_PROMPT, encoded);\n System.out.println(\"── Sending to AI ... ──\\n\");\n String aiResponse = callAi(apiKey, body);\n\n System.out.println(\"── AI Raw Response ──\");\n System.out.println(aiResponse);\n System.out.println();\n\n // Check placeholder preservation\n System.out.println(\"── Placeholder Preservation Check ──\");\n boolean has0 = aiResponse.contains(\"{0}\");\n boolean has1 = aiResponse.contains(\"{1}\");\n System.out.println(\" {0} preserved: \" + (has0 ? \"YES\" : \"NO ❌\"));\n System.out.println(\" {1} preserved: \" + (has1 ? \"YES\" : \"NO ❌\"));\n\n // Count: {0}/{1} should appear exactly once each (AI must not duplicate/drop)\n int count0 = countOccurrence(aiResponse, \"{0}\");\n int count1 = countOccurrence(aiResponse, \"{1}\");\n System.out.println(\" {0} count: \" + count0 + \" (expected 1)\");\n System.out.println(\" {1} count: \" + count1 + \" (expected 1)\");\n\n // Decode\n TspParser parser = new TspParser(TspRecovery.Level.V1);\n TspParser.ParseResult parsed = parser.parse(aiResponse);\n TspDecoder decoder = new TspDecoder(registry);\n List decoded = decoder.decode(parsed);\n\n // fillNumbers: {0} -> +0.28, {1} -> 100\n System.out.println(\"\\n── After fillNumbers (placeholders -> real numbers) ──\");\n for (StyledSegment seg : decoded) {\n String filled = fillNumbers(seg.text());\n String color = seg.isPlain() ? colorName(encoder.defaultStyle()) + \"(default)\"\n : colorName(seg.style()) + \"(ID)\";\n System.out.printf(\" %-14s | \\\"%s\\\"%n\", color, filled);\n }\n\n // Verify numbers restored correctly\n System.out.println(\"\\n── Number Restoration Check ──\");\n String allFilled = decoded.stream().map(s -> fillNumbers(s.text()))\n .reduce(\"\", String::concat);\n boolean num0Ok = allFilled.contains(VALS[0]); // +0.28\n boolean num1Ok = allFilled.contains(VALS[1]); // 100\n System.out.println(\" {0} -> \\\"\" + VALS[0] + \"\\\" restored: \" + (num0Ok ? \"YES ✅\" : \"NO ❌\"));\n System.out.println(\" {1} -> \\\"\" + VALS[1] + \"\\\" restored: \" + (num1Ok ? \"YES ✅\" : \"NO ❌\"));\n\n // Color check for the number segments\n System.out.println(\"\\n── Color Check (number segments) ──\");\n for (StyledSegment seg : decoded) {\n String t = fillNumbers(seg.text());\n if (t.contains(VALS[0])) {\n System.out.printf(\" \\\"%s\\\" color = %s (should be GREEN) %s%n\",\n VALS[0], colorName(seg.style()),\n seg.style().equals(GREEN) ? \"✅\" : \"❌\");\n }\n if (t.contains(VALS[1]) && !t.contains(VALS[0])) {\n System.out.printf(\" \\\"%s\\\" color = %s (should be GRAY/default) %s%n\",\n VALS[1], seg.isPlain() ? \"GRAY(default)\" : colorName(seg.style()),\n seg.isPlain() || seg.style().equals(GRAY) ? \"✅\" : \"❌\");\n }\n }\n\n System.out.println(\"\\n── Verdict ──\");\n if (has0 && has1 && count0 == 1 && count1 == 1 && num0Ok && num1Ok) {\n System.out.println(\" ✅ Placeholders preserved, numbers restored correctly, colors correct.\");\n } else {\n System.out.println(\" ⚠ Issues found (see above).\");\n }\n }\n\n private static String fillNumbers(String text) {\n String r = text;\n for (int i = 0; i < VALS.length; i++) r = r.replace(\"{\" + i + \"}\", VALS[i]);\n return r;\n }\n\n private static int countOccurrence(String s, String sub) {\n int c = 0, idx = 0;\n while ((idx = s.indexOf(sub, idx)) != -1) { c++; idx += sub.length(); }\n return c;\n }\n\n private static String colorName(Style s) {\n if (s == null || s.isEmpty()) return \"EMPTY\";\n return switch (s.colorHex()) {\n case \"#AAAAAA\" -> \"GRAY\";\n case \"#55FF55\" -> \"GREEN\";\n case \"#55FFFF\" -> \"AQUA\";\n default -> s.colorHex();\n };\n }\n\n // ---- HTTP ----\n private static String callAi(String apiKey, String body) throws Exception {\n HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();\n HttpRequest req = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.deepseek.com/chat/completions\"))\n .timeout(Duration.ofSeconds(180))\n .header(\"Authorization\", \"Bearer \" + apiKey)\n .header(\"Content-Type\", \"application/json\")\n .POST(HttpRequest.BodyPublishers.ofString(body))\n .build();\n HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());\n if (resp.statusCode() != 200) return \"HTTP \" + resp.statusCode();\n return extractContent(resp.body());\n }\n\n private static String extractContent(String json) {\n int k = json.indexOf(\"\\\"content\\\"\");\n if (k == -1) return json;\n int c = json.indexOf(':', k);\n int q = json.indexOf('\"', c + 1);\n if (q == -1) return json;\n StringBuilder sb = new StringBuilder();\n for (int i = q + 1; i < json.length(); i++) {\n char ch = json.charAt(i);\n if (ch == '\\\\' && i + 1 < json.length()) {\n sb.append(switch (json.charAt(i + 1)) {\n case 'n' -> '\\n'; case 't' -> '\\t'; case '\"' -> '\"';\n case '\\\\' -> '\\\\'; default -> json.charAt(i + 1);\n });\n i++;\n } else if (ch == '\"') break;\n else sb.append(ch);\n }\n return sb.toString();\n }\n\n private static String openAiBody(String model, String system, String user) {\n return \"{\\\"model\\\":\\\"\" + model + \"\\\",\\\"messages\\\":[{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"\"\n + escape(system) + \"\\\"},{\\\"role\\\":\\\"user\\\",\\\"content\\\":\\\"\" + escape(user)\n + \"\\\"}],\\\"temperature\\\":0.3,\\\"thinking\\\":{\\\"type\\\":\\\"disabled\\\"}}\";\n }\n\n private static String escape(String s) {\n return s.replace(\"\\\\\", \"\\\\\\\\\").replace(\"\\\"\", \"\\\\\\\"\").replace(\"\\n\", \"\\\\n\");\n }\n}\n"}, "4a265bd0e7689e73a93e44ac41cf5350d08b8450": {"t": 1785760113.0459511, "n": 9438, "h": "645a21424102fe68", "c": "package tsp.tests;\n\nimport tsp.*;\nimport java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * TSP + number placeholder ({0}{1}) protection test.\n * Verifies AI preserves {N} placeholders and fillNumbers restores correct numbers.\n * Run: java tsp.tests.TspPlaceholderTest \n */\npublic final class TspPlaceholderTest {\n\n private static final Style GRAY = Style.of(\"#AAAAAA\");\n private static final Style GREEN = Style.of(\"#55FF55\");\n private static final Style AQUA = Style.of(\"#55FFFF\");\n\n // Tusk Luck paragraph (4 lore lines). Numbers ALREADY replaced with {0}{1} placeholders.\n // {0} = \"+0.28\" (green), {1} = \"100\" (gray/default)\n private static final String[] VALS = {\"+0.28\", \"100\"};\n\n private static final List PARAGRAPH = List.of(\n new StyledSegment(\"Gain \", GRAY),\n new StyledSegment(\"{0} Magic Find \", GREEN), // {0} = +0.28\n new StyledSegment(\" for every\\n\", GRAY),\n new StyledSegment(\"{1} \", GRAY), // {1} = 100 (default color)\n new StyledSegment(\"Mining Fortune\", AQUA),\n new StyledSegment(\", doubled in the\\n\", GRAY),\n new StyledSegment(\"Glacite Tunnels \", GRAY),\n new StyledSegment(\"and \", GRAY),\n new StyledSegment(\"Glacite\", AQUA),\n new StyledSegment(\"\\nMineshafts\", GRAY),\n new StyledSegment(\".\", GRAY)\n );\n\n private static final String SYSTEM_PROMPT =\n \"You are a Minecraft item tooltip translator. Translate to Simplified Chinese. \" +\n \"[[NUMBER||TEXT]] tokens: NUMBER is a style ID PERMANENTLY BOUND to its content. \" +\n \" rule: [[N||X]] MUST become [[N||translated X]] - N stays with original content. \" +\n \"NEVER move content to a different NUMBER. NEVER merge or split tokens. \" +\n \"{0} {1} etc. are NUMBER placeholders - output them LITERALLY, never fill, remove, or translate them. \" +\n \"Reorder whole tokens freely. Output ONLY the translated text, no markdown.\";\n\n public static void main(String[] args) throws Exception {\n if (args.length < 1) {\n System.err.println(\"Usage: java tsp.tests.TspPlaceholderTest \");\n System.exit(1);\n }\n String apiKey = args[0];\n\n System.out.println(\"=== TSP + Number Placeholder Test ===\\n\");\n System.out.println(\"vals: {0}=\\\"\" + VALS[0] + \"\\\", {1}=\\\"\" + VALS[1] + \"\\\"\\n\");\n\n // Encode\n TspRegistry registry = new TspRegistry();\n TspEncoder encoder = TspEncoder.withAutoDefault(registry, PARAGRAPH);\n String encoded = encoder.encode(PARAGRAPH);\n\n System.out.println(\"── TSP Encoded (numbers -> {N} placeholders) ──\");\n System.out.println(encoded);\n System.out.println(\"\\n── Registry ──\");\n for (int i = 0; i < registry.size(); i++) {\n System.out.printf(\" ID %d -> %s%n\", i, colorName(registry.getStyle(i)));\n }\n System.out.println(\" default -> \" + colorName(encoder.defaultStyle()) + \"\\n\");\n\n // Send to AI\n String body = openAiBody(\"deepseek-v4-flash\", SYSTEM_PROMPT, encoded);\n System.out.println(\"── Sending to AI ... ──\\n\");\n String aiResponse = callAi(apiKey, body);\n\n System.out.println(\"── AI Raw Response ──\");\n System.out.println(aiResponse);\n System.out.println();\n\n // Check placeholder preservation\n System.out.println(\"── Placeholder Preservation Check ──\");\n boolean has0 = aiResponse.contains(\"{0}\");\n boolean has1 = aiResponse.contains(\"{1}\");\n System.out.println(\" {0} preserved: \" + (has0 ? \"YES\" : \"NO ❌\"));\n System.out.println(\" {1} preserved: \" + (has1 ? \"YES\" : \"NO ❌\"));\n\n // Count: {0}/{1} should appear exactly once each (AI must not duplicate/drop)\n int count0 = countOccurrence(aiResponse, \"{0}\");\n int count1 = countOccurrence(aiResponse, \"{1}\");\n System.out.println(\" {0} count: \" + count0 + \" (expected 1)\");\n System.out.println(\" {1} count: \" + count1 + \" (expected 1)\");\n\n // Decode\n TspParser parser = new TspParser(TspRecovery.Level.V1);\n TspParser.ParseResult parsed = parser.parse(aiResponse);\n TspDecoder decoder = new TspDecoder(registry);\n List decoded = decoder.decode(parsed);\n\n // fillNumbers: {0} -> +0.28, {1} -> 100\n System.out.println(\"\\n── After fillNumbers (placeholders -> real numbers) ──\");\n for (StyledSegment seg : decoded) {\n String filled = fillNumbers(seg.text());\n String color = seg.isPlain() ? colorName(encoder.defaultStyle()) + \"(default)\"\n : colorName(seg.style()) + \"(ID)\";\n System.out.printf(\" %-14s | \\\"%s\\\"%n\", color, filled);\n }\n\n // Verify numbers restored correctly\n System.out.println(\"\\n── Number Restoration Check ──\");\n String allFilled = decoded.stream().map(s -> fillNumbers(s.text()))\n .reduce(\"\", String::concat);\n boolean num0Ok = allFilled.contains(VALS[0]); // +0.28\n boolean num1Ok = allFilled.contains(VALS[1]); // 100\n System.out.println(\" {0} -> \\\"\" + VALS[0] + \"\\\" restored: \" + (num0Ok ? \"YES ✅\" : \"NO ❌\"));\n System.out.println(\" {1} -> \\\"\" + VALS[1] + \"\\\" restored: \" + (num1Ok ? \"YES ✅\" : \"NO ❌\"));\n\n // Color check for the number segments\n System.out.println(\"\\n── Color Check (number segments) ──\");\n for (StyledSegment seg : decoded) {\n String t = fillNumbers(seg.text());\n if (t.contains(VALS[0])) {\n System.out.printf(\" \\\"%s\\\" color = %s (should be GREEN) %s%n\",\n VALS[0], colorName(seg.style()),\n seg.style().equals(GREEN) ? \"✅\" : \"❌\");\n }\n if (t.contains(VALS[1]) && !t.contains(VALS[0])) {\n System.out.printf(\" \\\"%s\\\" color = %s (should be GRAY/default) %s%n\",\n VALS[1], seg.isPlain() ? \"GRAY(default)\" : colorName(seg.style()),\n seg.isPlain() || seg.style().equals(GRAY) ? \"✅\" : \"❌\");\n }\n }\n\n System.out.println(\"\\n── Verdict ──\");\n if (has0 && has1 && count0 == 1 && count1 == 1 && num0Ok && num1Ok) {\n System.out.println(\" ✅ Placeholders preserved, numbers restored correctly, colors correct.\");\n } else {\n System.out.println(\" ⚠ Issues found (see above).\");\n }\n }\n\n private static String fillNumbers(String text) {\n String r = text;\n for (int i = 0; i < VALS.length; i++) r = r.replace(\"{\" + i + \"}\", VALS[i]);\n return r;\n }\n\n private static int countOccurrence(String s, String sub) {\n int c = 0, idx = 0;\n while ((idx = s.indexOf(sub, idx)) != -1) { c++; idx += sub.length(); }\n return c;\n }\n\n private static String colorName(Style s) {\n if (s == null || s.isEmpty()) return \"EMPTY\";\n return switch (s.colorHex()) {\n case \"#AAAAAA\" -> \"GRAY\";\n case \"#55FF55\" -> \"GREEN\";\n case \"#55FFFF\" -> \"AQUA\";\n default -> s.colorHex();\n };\n }\n\n // ---- HTTP ----\n private static String callAi(String apiKey, String body) throws Exception {\n HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();\n HttpRequest req = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.deepseek.com/chat/completions\"))\n .timeout(Duration.ofSeconds(180))\n .header(\"Authorization\", \"Bearer \" + apiKey)\n .header(\"Content-Type\", \"application/json\")\n .POST(HttpRequest.BodyPublishers.ofString(body))\n .build();\n HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());\n if (resp.statusCode() != 200) return \"HTTP \" + resp.statusCode();\n return extractContent(resp.body());\n }\n\n private static String extractContent(String json) {\n int k = json.indexOf(\"\\\"content\\\"\");\n if (k == -1) return json;\n int c = json.indexOf(':', k);\n int q = json.indexOf('\"', c + 1);\n if (q == -1) return json;\n StringBuilder sb = new StringBuilder();\n for (int i = q + 1; i < json.length(); i++) {\n char ch = json.charAt(i);\n if (ch == '\\\\' && i + 1 < json.length()) {\n sb.append(switch (json.charAt(i + 1)) {\n case 'n' -> '\\n'; case 't' -> '\\t'; case '\"' -> '\"';\n case '\\\\' -> '\\\\'; default -> json.charAt(i + 1);\n });\n i++;\n } else if (ch == '\"') break;\n else sb.append(ch);\n }\n return sb.toString();\n }\n\n private static String openAiBody(String model, String system, String user) {\n return \"{\\\"model\\\":\\\"\" + model + \"\\\",\\\"messages\\\":[{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"\"\n + escape(system) + \"\\\"},{\\\"role\\\":\\\"user\\\",\\\"content\\\":\\\"\" + escape(user)\n + \"\\\"}],\\\"temperature\\\":0.3,\\\"thinking\\\":{\\\"type\\\":\\\"disabled\\\"}}\";\n }\n\n private static String escape(String s) {\n return s.replace(\"\\\\\", \"\\\\\\\\\").replace(\"\\\"\", \"\\\\\\\"\").replace(\"\\n\", \"\\\\n\");\n }\n}\n"}}}}