สร้างตัวแก้ปริศนาด้านบนด้านหน้า


15

ปริศนาบนสุดด้านหน้าเป็นปริศนาที่คุณจะต้องสร้างรูปร่าง 3-D ของบล็อก (ปกติลูกบาศก์) ที่กำหนดมุมมองสามมุมฉาก: มุมมองด้านบนมุมมองด้านหน้าและมุมมองด้านข้าง

ตัวอย่างเช่นกำหนดมุมมองด้านบนด้านหน้าและด้านข้างดังนี้:

Top:        Front:      Side:
. . . .     . . . .     . . . .
. x x .     . x x .     . x x .
. x x .     . x x .     . x x .
. . . .     . . . .     . . . .

In this problem, the side view is taken from the right.

ก้อน 2x2x2 (ที่มีเล่ม 8) จะทำให้โซลูชันนี้พอใจ แต่มันสามารถทำได้ในเล่มที่ 4 ถ้าเรามีโครงสร้างเลเยอร์ต่อไปนี้:

. . . .     . . . .
. x . .     . . x .
. . x .     . x . .
. . . .     . . . .

นอกจากนี้ยังมีข้อตกลงที่แก้ไม่ได้บางอย่าง ยกตัวอย่างเช่น

Top:        Front:      Side:
. . . .     . . . .     . . . .
. . . .     . . x .     . . . .
. x . .     . . . .     . x . .
. . . .     . . . .     . . . .

หากมุมมองด้านบนบอกว่าบล็อกนั้นเป็นวินาทีจากด้านซ้ายจะไม่มีวิธีใดที่สามารถจับคู่มุมมองด้านหน้าที่บอกว่าบล็อกนั้นต้องอยู่ในอันดับที่สามจากด้านซ้าย ดังนั้นการจัดการนี้เป็นไปไม่ได้


งานของคุณคือการสร้างโปรแกรมที่ให้ตัวต่อแบบ 4x4 ด้านหน้าตัวต่อพยายามที่จะแก้ปัญหาในจำนวนที่น้อยที่สุดของคิวบ์หรือประกาศว่าแก้ไม่ได้

โปรแกรมของคุณจะป้อนข้อมูลเป็นชุดจำนวน 48 บิตซึ่งแสดงถึงมุมมองด้านบนด้านหน้าและด้านข้าง อาจอยู่ในรูปแบบที่คุณต้องการ (สตริง 6 ไบต์, สตริงของ 0 และ 1, เลขฐานสิบหก 12 หลัก, ฯลฯ ) แต่ลำดับของบิตจะต้องแมปดังนี้:

Top: 0x00   Front: 0x10 Side: 0x20
0 1 2 3     0 1 2 3     0 1 2 3
4 5 6 7     4 5 6 7     4 5 6 7
8 9 a b     8 9 a b     8 9 a b
c d e f     c d e f     c d e f

กล่าวอีกนัยหนึ่งบิตจะเรียงตามลำดับจากซ้ายไปขวาเรียงจากบนลงล่างจากด้านบนจากนั้นด้านหน้าและจากนั้นดูจากด้านข้าง

จากนั้นโปรแกรมของคุณจะแสดงผลเป็นชุด 64 บิตซึ่งระบุคิวบ์ในกริด 4x4x4 ที่กรอกข้อมูลหรือระบุว่ากริดไม่สามารถแก้ไขได้


โปรแกรมของคุณจะได้คะแนนจากการใช้งานแบตเตอรี่ 1,000,000 กรณีทดสอบ

ข้อมูลการทดสอบจะถูกสร้างขึ้นโดยทำการแฮ็ค MD5 ของจำนวนเต็ม "000000" ถึง "999999" เป็นสตริงการแยก 48 บิตแรก (12 ฐานสิบหก) ของแต่ละแฮชเหล่านี้และใช้เป็นอินพุตสำหรับด้านหน้า - ปริศนาด้านข้าง ดังตัวอย่างต่อไปนี้เป็นอินพุตการทดสอบและปริศนาที่สร้าง:

Puzzle seed: 000000   hash: 670b14728ad9
Top:        Front:      Side:
. x x .     . . . x     x . . .
x x x x     . x . x     x . x .
. . . .     . x x x     x x . x
x . x x     . . x .     x . . x

Puzzle seed: 000001   hash: 04fc711301f3
Top:        Front:      Side:
. . . .     . x x x     . . . .
. x . .     . . . x     . . . x
x x x x     . . . x     x x x x
x x . .     . . x x     . . x x

Puzzle seed: 000157   hash: fe88e8f9b499
Top:        Front:      Side:
x x x x     x x x .     x . x x
x x x .     x . . .     . x . .
x . . .     x x x x     x . . x
x . . .     x . . x     x . . x

สองรายการแรกไม่สามารถแก้ไขได้ในขณะที่รายการสุดท้ายมีโซลูชันที่มีเลเยอร์ต่อไปนี้ด้านหน้าไปด้านหลัง:

x . . .   . . . .   x x x .   x x x .
. . . .   x . . .   . . . .   . . . .
x . . .   . . . .   . . . .   x x x x
x . . .   . . . .   . . . .   x . . x

There are a total of 16 blocks here, but it can probably be done in less.

คะแนนโปรแกรมของคุณจะถูกกำหนดโดยเกณฑ์ต่อไปนี้ตามลำดับความสำคัญจากมากไปน้อย:

  • จำนวนคดีที่ได้รับการแก้ไขสูงสุด
  • จำนวนบล็อกต่ำสุดที่ต้องใช้เพื่อแก้ไขกรณีเหล่านั้น
  • รหัสที่สั้นที่สุดในหน่วยไบต์

คุณต้องส่งและคำนวณคะแนนด้วยตัวเองซึ่งต้องใช้โปรแกรมของคุณเพื่อให้สามารถดำเนินการผ่าน 1,000,000 กรณีทดสอบทั้งหมด


ฉันได้เรียนรู้ในขณะที่พยายามสร้างกรณีทดสอบสำหรับปัญหานี้ว่ามีอีกหลายกรณีที่แก้ไม่ตกไม่ได้ ฉันสงสัยว่าสิ่งนี้จะเปิดออก
Joe Z.

หากเป็นปัญหาการปรับให้เหมาะสมควรมีการ จำกัด เวลาดังนั้นผู้คนจึงไม่บังคับให้ทำเช่นนั้น
isaacg

อย่างไรก็ตามการ จำกัด เวลาใช้เวลานานในการทดสอบ โซลูชันที่ใช้เวลานานในการทำงานจะไม่สร้างผลลัพธ์ที่สามารถโพสต์ได้ที่นี่ นั่นคือสิ่งที่ปัญหาการเพิ่มประสิทธิภาพทั้งหมดที่ฉันเขียนทำงาน
Joe Z.

1
ที่เกี่ยวข้อง: codegolf.stackexchange.com/questions/34547/…
John Dvorak

1
@JoeZ orlp ถูกต้อง ฉันมีข้อบกพร่องในการแปลง md5 เป็นปริศนา 551 ปริศนาที่แก้ได้ใน 00000-99999 และ 5360 ปริศนาที่แก้ไขได้ใน 000000-999999
Jakube

คำตอบ:


5

Python: 5360 กรณีทดสอบแก้ไขโดยใช้ 69519 บล็อก, 594 ไบต์

นี่ควรเป็นค่าที่เหมาะสมที่สุด

วิธีการ:

ก่อนอื่นฉันจะทดสอบว่ากรณีทดสอบแก้ไขได้จริงหรือไม่ ฉันทำสิ่งนี้โดยการเริ่มต้นรายการความยาว 64 โดยหนึ่งและตั้งตำแหน่งทั้งหมดเป็นศูนย์หากมีพิกเซลที่สอดคล้องกันของสามมุมมองเป็นศูนย์ จากนั้นฉันก็ดูภาพปริศนาจากทั้ง 3 ทิศทางและดูว่ามุมมองนั้นเหมือนกับมุมมองอินพุตหรือไม่ หากมีค่าเท่ากันปริศนาดังกล่าวสามารถแก้ไขได้ (เราพบวิธีแก้ปัญหาที่เลวร้ายที่สุดแล้ว) ไม่เช่นนั้นจะแก้ไม่ตก

จากนั้นฉันใช้วิธีการแบบสาขาและแบบ จำกัด สำหรับการค้นหาโซลูชันที่ดีที่สุด

การแตกกิ่ง:ฉันมีฟังก์ชั่นวนซ้ำ ความลึกของการเรียกซ้ำจะบอกจำนวนค่าที่แก้ไขแล้ว ในการเรียกใช้ฟังก์ชั่นแต่ละครั้งฉันเรียกตัวเองสองครั้งถ้าค่าที่ดัชนีปัจจุบันคือ 1 (ค่านี้สามารถเป็น 0 หรือ 1 ในการแก้ปัญหาที่ดีที่สุด) หรือหนึ่งครั้งหากค่าเป็น 0 (จะต้องมีศูนย์ใน ทางออกที่ดีที่สุด)

การผูกมัด:ฉันใช้สองกลยุทธ์ที่แตกต่างกันที่นี่

  1. ฉันคำนวณมุมมองจาก 3 ด้านในการเรียกใช้ฟังก์ชั่นแต่ละครั้งและดูหากพวกเขายังคงตรงกับค่าอินพุต หากพวกเขาไม่ตรงกันฉันจะไม่เรียกใช้ฟังก์ชันซ้ำ

  2. ฉันรักษาทางออกที่ดีที่สุดในความทรงจำ มีสาขาที่แก้ไขแล้วในสาขาปัจจุบันมากกว่าทางออกที่ดีที่สุดฉันสามารถปิดสาขานั้นได้แล้ว นอกจากนี้ฉันสามารถประมาณขอบเขตล่างสำหรับจำนวนบล็อกที่เปิดใช้งานซึ่งไม่ได้รับการแก้ไข และเงื่อนไขจะกลายเป็น#number of activated fixed blocks + #lower bound of activated blocks (under the not fixed blocks) < #number of activated blocks in the best solution.

นี่คือรหัสไพ ธ อน มันกำหนดฟังก์ชั่นfซึ่งคาดว่า 3 รายการที่มี 1s และ 0s และส่งกลับ 0 (ไม่สามารถแก้ไขได้) หรือรายการ 1s และ 0s แสดงถึงทางออกที่ดีที่สุด

S=sum;r=range
def f(t,f,s):
 for i in r(4):s[4*i:4*i+4]=s[4*i:4*i+4][::-1]
 c=[min(t[i%16],f[(i//16)*4+i%4],s[i//4])for i in r(64)]
 m=lambda:([int(S(c[i::16])>0)for i in r(16)],[int(S(c[i+j:i+j+16:4])>0)for i in r(0,64,16)for j in r(4)],[int(S(c[i+j:i+j+4])>0)for i in r(0,64,16)for j in r(0,16,4)])==(t,f,s)
 Z=[65,0];p=[]
 def g(k):
  if k>63and S(c)<Z[0]:Z[:]=[S(c),c[:]]
  if k>63or S(c[:k])+p[k]>=Z[0]:return
  if c[k]:c[k]=0;m()and g(k+1);c[k]=1
  m()and g(k+1)
 for i in r(64):h,R=(i//16)*4+4,(i//4)%4;p+=[max(S(f[h:]),S(s[h:]))+max((R<1)*S(f[h+i%4-3:h]),S(s[h+R-3:h]))]
 g(0);return Z[1]

ความยาวรหัส 596 ไบต์ / ตัวอักษร และนี่คือกรอบการทดสอบ:

from hashlib import md5
from time import time

N = 1000000
start=time();count=blocks=0
for n in range(N):
 bits = list(map(int,"{:048b}".format(int(md5("{:06}".format(n).encode("utf-8")).hexdigest()[:12], 16))))
 result = f(bits[:16], bits[16:32], bits[32:])
 if result:
  count += 1
  blocks += sum(result)
  print("Seed: {:06}, blocks: {}, cube: {}".format(n, sum(result), result))
print()
print("{} out of {} puzzles are solvable using {} blocks.".format(count, N, blocks))
print("Total time: {:.2f} seconds".format(time()-start))

คุณสามารถค้นหารุ่น ungolfed ของโปรแกรมที่นี่ นอกจากนี้ยังเร็วกว่าเล็กน้อย

ผล:

ไขปริศนา 5360 จาก 1000000 แก้ได้ รวมแล้วเราต้องการ 69519 บล็อก จำนวนบล็อกแตกต่างกันจาก 6 บล็อกถึง 18 บล็อก ปริศนาที่ยากที่สุดใช้เวลาในการแก้ประมาณ 1 วินาที มันเป็นปริศนาที่มีเมล็ด"347177"ซึ่งดูเหมือน

Top:      Front:    Side:
x x . .   x x x x   x . x .
x x x x   x x x x   x x x x
x x x x   x x x x   x x x x
x x . x   x x x x   x . x x

และมีทางออกที่ดีที่สุดด้วย 18 ก้อน ต่อไปนี้เป็นบางส่วนจากด้านบนสำหรับแต่ละเลเยอร์:

Top 1:    Top 2:    Top 3:    Top 4:
. . . .   . x . .   . x . .   x . . .
. . x x   . . x .   x . . .   . x x .
. . . .   . . . x   x x x .   . . . .
x x . .   x . . .   . . . x   . . . x

เวลาการทำงานทั้งหมดสำหรับกรณีทดสอบทั้งหมดอยู่ที่ประมาณ 90 วินาที ฉันใช้ PyPy เพื่อรันโปรแกรมของฉัน CPython (ล่าม Python เริ่มต้น) จะช้าลงเล็กน้อย แต่ก็แก้ปริศนาทั้งหมดได้ในเวลาเพียง 7 นาที

คุณสามารถค้นหาผลลัพธ์ที่สมบูรณ์ได้ที่นี่ : ผลลัพธ์เป็นแบบอธิบายตนเอง เช่นเอาต์พุตสำหรับตัวต่อปริศนาด้านบนคือ:

Seed: 347177, blocks: 18, cube: [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1]

3

5360 เคสถูกแก้ไขด้วย 69519 บล็อก; 923 ไบต์

นี่เป็นสิ่งที่ดีที่สุด โทรหาอาเรย์ของคนและเลขศูนย์ โยน a NullPointerExceptionสำหรับอินพุตที่ไม่ถูกต้อง ประสิทธิภาพบางอย่างเสียสละเพื่อเล่นกอล์ฟ มันยังเสร็จสมบูรณ์ภายในเวลาอันสมควรสำหรับอินพุตทดสอบทั้งหมด 1000000

import java.util.*;int[]a(int[]a){a b=new a(a);b=b.b(64);return b.d();}class a{List<int[]>a=new ArrayList();List b=new ArrayList();int c;a(int[]d){int e=0,f,g,h,i[]=new int[48];for(;e<64;e++){f=e/16;g=(e%16)/4;h=e%4;if(d[f+12-4*h]+d[16+f+g*4]+d[32+h+g*4]>2){i[f+12-4*h]=i[16+f+g*4]=i[32+h+g*4]=1;a.add(new int[]{f,g,h});c++;}}if(!Arrays.equals(d,i))throw null;b=f();}a(){}a b(int d){if(c-b.size()>d|b.size()<1)return this;a e=c();e.a.remove(b.get(0));e.b.retainAll(e.f());e.c--;e=e.b(d);d=Math.min(e.c,d);a f=c();f=f.b(d);return e.c>f.c?f:e;}a c(){a c=new a();c.a=new ArrayList(a);c.b=new ArrayList(b);c.b.remove(0);c.c=this.c;return c;}int[]d(){int[]d=new int[64];for(int[]e:a)d[e[2]*16+e[1]*4+e[0]]=1;return d;}List f(){List d=new ArrayList();int e,f,g;for(int[]h:a){e=0;f=0;g=0;for(int[]p:a)if(p!=h){e|=h[0]==p[0]&h[1]==p[1]?1:0;f|=h[0]==p[0]&h[2]==p[2]?1:0;g|=h[1]==p[1]&h[2]==p[2]?1:0;}if(e+f+g>2)d.add(h);}return d;}}

กลยุทธ์:

จริง ๆ แล้วฉันเคยเล่นปริศนานี้มาไม่นานเมื่อฉันอายุ 10 ขวบวิธีนี้ใช้วิธีการของฉัน

ขั้นตอนที่ 1:

สร้างลูกบาศก์ด้วยบล็อกส่วนใหญ่ที่เหมาะกับมุมมองที่กำหนด

ขั้นตอนที่ 2:

สร้างรายการชิ้นส่วนที่ถอดออกได้ (ชิ้นส่วนใดที่มีอีกชิ้นหนึ่งอยู่ในแถวของมันคอลัมน์ของมันและคานของมัน)

ขั้นตอนที่ 3:

ทดสอบทุกวิธีที่เป็นไปได้ในการเก็บหรือลบชิ้นส่วนที่ถอดออกได้ (ผ่านเดรัจฉานบังคับด้วยซ้ำ)

ขั้นตอนที่ 4:

รักษาคิวบ์ที่ถูกต้องที่สุด

Ungolfed:

int[] main(int[] bits) {
    Cube cube = new Cube(bits);
    cube = cube.optimize(64);
    return cube.bits();
}

class Cube {

    List<int[]> points = new ArrayList();
    List removablePoints = new ArrayList();
    int size;

    Cube(int[] bits) {
        int i = 0,x,y,z,placed[] = new int[48];
        for (; i < 64; i++) {
            x = i / 16;
            y = (i % 16) / 4;
            z = i % 4;
            if (bits[x + 12 - 4 * z] + bits[16 + x + y * 4] + bits[32 + z + y * 4] > 2) {
                placed[x + 12 - 4 * z] = placed[16 + x + y * 4] = placed[32 + z + y * 4] = 1;
                points.add(new int[]{x, y, z});
                size++;
            }
        }

        if (!Arrays.equals(bits, placed))
            throw null;

        removablePoints = computeRemovablePoints();
    }

    Cube() {
    }

    Cube optimize(int smallestFound) {
        if (size - removablePoints.size() > smallestFound | removablePoints.size() < 1)
            return this;

        Cube cube1 = duplicate();
        cube1.points.remove(removablePoints.get(0));

        cube1.removablePoints.retainAll(cube1.computeRemovablePoints());
        cube1.size--;

        cube1 = cube1.optimize(smallestFound);
        smallestFound = Math.min(cube1.size, smallestFound);

        Cube cube2 = duplicate();

        cube2 = cube2.optimize(smallestFound);

        return cube1.size > cube2.size ? cube2 : cube1;

    }

    Cube duplicate() {
        Cube c = new Cube();
        c.points = new ArrayList(points);
        c.removablePoints = new ArrayList(removablePoints);
        c.removablePoints.remove(0);
        c.size = size;
        return c;
    }

    int[] bits() {
        int[] bits = new int[64];
        for (int[] point : points)
            bits[point[2] * 16 + point[1] * 4 + point[0]] = 1;
        return bits;
    }

    List computeRemovablePoints(){
        List removablePoints = new ArrayList();
        int removableFront, removableTop, removableSide;
        for (int[] point : points) {
            removableFront = 0;
            removableTop = 0;
            removableSide = 0;
            for (int[] p : points)
                if (p != point) {
                    removableFront |= point[0] == p[0] & point[1] == p[1] ? 1 : 0;
                    removableTop |= point[0] == p[0] & point[2] == p[2] ? 1 : 0;
                    removableSide |= point[1] == p[1] & point[2] == p[2] ? 1 : 0;
                }
            if (removableFront + removableTop + removableSide > 2)
                removablePoints.add(point);
        }
        return removablePoints;
    }

    public String toString() {

        String result = "";
        int bits[] = bits(),x,y,z;

        for (z = 0; z < 4; z++) {
            for (y = 0; y < 4; y++) {
                for (x = 0; x < 4; x++) {
                    result += bits[x + 4 * y + 16 * z] > 0 ? 'x' : '.';
                }
                result += System.lineSeparator();
            }
            result += System.lineSeparator();
        }

        return result;

    }
}

โปรแกรมเต็มรูปแบบ:

import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Example cube:
 *
 * origin
 * |   ........
 * |  .      ..
 * | . top  . .
 * v.      .  .
 * ........   .  <- side
 * .      .  .
 * . front. .
 * .      ..
 * ........
 *
 *     / z
 *    /
 *  /
 * .-----> x
 * |
 * |
 * |
 * V y
 */

public class PPCG48247 {

    public static void main(String[] args) throws Exception{
        MessageDigest digest = MessageDigest.getInstance("MD5");
        int totalSolved = 0;
        int totalCubes = 0;

        for (int i = 0; i < 1000000; i++){
            byte[] input = String.format("%06d", i).getBytes();

            byte[] hash = digest.digest(input);
            int[] bits = new int[48];

            for (int j = 0, k = 0; j < 6; j++, k += 8){
                byte b = hash[j];
                bits[k] = (b >> 7) & 1;
                bits[k + 1] = (b >> 6) & 1;
                bits[k + 2] = (b >> 5) & 1;
                bits[k + 3] = (b >> 4) & 1;
                bits[k + 4] = (b >> 3) & 1;
                bits[k + 5] = (b >> 2) & 1;
                bits[k + 6] = (b >> 1) & 1;
                bits[k + 7] = b & 1;
            }

            try {
                int[] solution = new PPCG48247().a(bits);
                totalSolved++;
                for (int b : solution){
                    totalCubes += b;
                }
            } catch (NullPointerException ignored){}

        }
        System.out.println(totalSolved);
        System.out.println(totalCubes);
    }

    int[] main(int[] bits) {
        Cube cube = new Cube(bits);
        cube = cube.optimize(64);
        return cube.bits();
    }

    class Cube {

        List<int[]> points = new ArrayList();
        List removablePoints = new ArrayList();
        int size;

        Cube(int[] bits) {
            int i = 0,x,y,z,placed[] = new int[48];
            for (; i < 64; i++) {
                x = i / 16;
                y = (i % 16) / 4;
                z = i % 4;
                if (bits[x + 12 - 4 * z] + bits[16 + x + y * 4] + bits[32 + z + y * 4] > 2) {
                    placed[x + 12 - 4 * z] = placed[16 + x + y * 4] = placed[32 + z + y * 4] = 1;
                    points.add(new int[]{x, y, z});
                    size++;
                }
            }

            if (!Arrays.equals(bits, placed))
                throw null;

            removablePoints = computeRemovablePoints();
        }

        Cube() {
        }

        Cube optimize(int smallestFound) {
            if (size - removablePoints.size() > smallestFound | removablePoints.size() < 1)
                return this;

            Cube cube1 = duplicate();
            cube1.points.remove(removablePoints.get(0));

            cube1.removablePoints.retainAll(cube1.computeRemovablePoints());
            cube1.size--;

            cube1 = cube1.optimize(smallestFound);
            smallestFound = Math.min(cube1.size, smallestFound);

            Cube cube2 = duplicate();

            cube2 = cube2.optimize(smallestFound);

            return cube1.size > cube2.size ? cube2 : cube1;

        }

        Cube duplicate() {
            Cube c = new Cube();
            c.points = new ArrayList(points);
            c.removablePoints = new ArrayList(removablePoints);
            c.removablePoints.remove(0);
            c.size = size;
            return c;
        }

        int[] bits() {
            int[] bits = new int[64];
            for (int[] point : points)
                bits[point[2] * 16 + point[1] * 4 + point[0]] = 1;
            return bits;
        }

        List computeRemovablePoints(){
            List removablePoints = new ArrayList();
            int removableFront, removableTop, removableSide;
            for (int[] point : points) {
                removableFront = 0;
                removableTop = 0;
                removableSide = 0;
                for (int[] p : points)
                    if (p != point) {
                        removableFront |= point[0] == p[0] & point[1] == p[1] ? 1 : 0;
                        removableTop |= point[0] == p[0] & point[2] == p[2] ? 1 : 0;
                        removableSide |= point[1] == p[1] & point[2] == p[2] ? 1 : 0;
                    }
                if (removableFront + removableTop + removableSide > 2)
                    removablePoints.add(point);
            }
            return removablePoints;
        }

        public String toString() {

            String result = "";
            int bits[] = bits(),x,y,z;

            for (z = 0; z < 4; z++) {
                for (y = 0; y < 4; y++) {
                    for (x = 0; x < 4; x++) {
                        result += bits[x + 4 * y + 16 * z] > 0 ? 'x' : '.';
                    }
                    result += System.lineSeparator();
                }
                result += System.lineSeparator();
            }

            return result;

        }
    }

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.