แปลงสตริงสัญกรณ์ Forsyth-Edwards เป็นรูปแบบ ASCII


9

ในหมากรุกฟอร์ซิ ธ - เอ็ดเวิร์ดสัญกรณ์มากกว่าปกติเรียกว่า "เฟน" เป็นข้อความที่ถ่ายทอดทางบอร์ด มันอธิบายถึงแปดแถวของกระดาน (เรียกว่า "อันดับ" ในหมากรุก) จากบนลงล่างจากมุมมองของไวท์ ชิ้นส่วนจะถูกเขียนเป็น K (ราชา), Q (ราชินี), R (โกง), B (บาทหลวง), N (อัศวิน) และ P (จำนำ) ชิ้นส่วนสีดำใช้ตัวอักษรเหล่านี้เป็นตัวพิมพ์เล็กและชิ้นส่วนสีขาวจะใช้ตัวอักษรเหล่านี้เป็นตัวพิมพ์ใหญ่ ช่องว่างจะถูกระบุด้วยตัวเลขตั้งแต่ 1 ถึง 8 เพื่อระบุจำนวนช่องว่างที่ต่อเนื่องกัน อันดับที่ว่างเปล่าจะ8เป็นเกมหมากรุกสีดำเดียวในคอลัมน์ขวาสุด (เรียกว่า "ไฟล์" ในเกมหมากรุก) จะเป็นและสองเบี้ยสีขาวที่ปลายแถวที่แต่ละคนจะเป็น7r PP4PPอันดับจะถูกคั่นด้วย/. มีเป็นปกติข้อมูลอื่น ๆ ที่เพิ่มเข้ามาแสดงให้เห็นที่ด้านข้างคือการย้ายใช้เรือและการกินสิทธิจำนวนเคลื่อนย้ายและนาฬิกา halfmove แต่เราจะไม่สนใจพวกเขาสำหรับวัตถุประสงค์ของการท้าทายนี้

อินพุต

สตริง FEN จากบรรทัดคำสั่งหรือ STDIN ตามที่คุณต้องการ คุณอาจคิดว่าสายนี้ถูกต้องเสมอ

เอาท์พุต

เขียนถึง STDOUT การแสดงผลงานศิลปะ ASCII แบบง่าย ๆ ของบอร์ดตามที่ปรากฏจริง:

  • ชิ้นจะแสดงโดยตัวละครของพวกเขาใน FEN
  • สี่เหลี่ยมที่ว่างเปล่าจะถูกแทนด้วยช่องว่าง
  • ชิ้นและสี่เหลี่ยมจะถูกคั่นด้วยท่อ|และมีท่อที่ด้านข้างของกระดาน

ดังนั้นกระดานว่างเปล่าที่เขียนเหมือน8/8/8/8/8/8/8/8ใน FEN จะปรากฏเป็น

| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |

ตำแหน่งเริ่มต้นของเกมหมากรุกถูกเขียนขึ้นrnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNRและจะปรากฏเป็น

|r|n|b|q|k|b|n|r|
|p|p|p|p|p|p|p|p|
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
|P|P|P|P|P|P|P|P|
|R|N|B|Q|K|B|N|R|

ตำแหน่งสุดท้ายของAnderssen-Kieseritzky 1851เรียกว่า "The Immortal Game" ในชุมชนหมากรุกถูกเขียนขึ้นr1bk3r/p2pBpNp/n4n2/1p1NP2P/6P1/3P4/P1P1K3/q5b1และโปรแกรมของคุณเมื่อป้อนข้อมูลนั้นจะส่งออก:

|r| |b|k| | | |r|
|p| | |p|B|p|N|p|
|n| | | | |n| | |
| |p| |N|P| | |P|
| | | | | | |P| |
| | | |P| | | | |
|P| |P| |K| | | |
|q| | | | | |b| |

เป็นที่ยอมรับหรือไม่ที่จะเขียนฟังก์ชั่นที่รับอินพุตและส่งคืนเอาต์พุตแทนที่จะเขียนลงใน STDOUT?
คดีของกองทุนโมนิกา

@QPaysTaxes ตามค่าเริ่มต้นเราอนุญาตและวิธีการแก้ปัญหาหลายอย่างที่ทำอยู่แล้ว ท้ายที่สุดมันก็ขึ้นอยู่กับ OP แม้ว่ามันจะไม่จำเป็นที่จะต้องแทนที่ค่าเริ่มต้นของเราในกรณีนี้
Alex A.

2
คำตอบที่คุณยอมรับไม่ใช่คำตอบที่สั้นที่สุด โดยไม่คำนึงถึงความรู้สึกของคุณต่อภาษากอล์ฟรหัสกอล์ฟหมายความว่ารหัสที่สั้นที่สุดชนะ
เดนนิส

3
นอกจากนี้คุณยังไม่สามารถลงโทษพวกเขาหรือยอมรับคำตอบโดยพลการ เว็บไซต์ทั้งหมดถูกสร้างขึ้นตามเกณฑ์การชนะอย่างมีวัตถุประสงค์
Dennis

1
+1สำหรับความท้าทายที่น่าสนใจ -2ที่ยอมรับคำตอบที่ผิดโดยไม่มีเหตุผลที่ดี
James

คำตอบ:


9

Perl, 28 ไบต์

รวมถึง +2 สำหรับ -lp

ให้อินพุตบน STDIN

fen.pl <<< "r1bk3r/p2pBpNp/n4n2/1p1NP2P/6P1/3P4/P1P1K3/q5b1"

fen.pl:

#!/usr/bin/perl -lp
s/\d/$"x$&/eg;s/|/|/g;y;/;

จริง ๆ แล้วในลีกของภาษากอล์ฟบาง ...

ขอให้สังเกตว่ารุ่นของไฟล์นั้นต้องการบรรทัดใหม่ขั้นสุดท้ายในไฟล์เพื่อให้มีขนาด 29 ไบต์ แต่เวอร์ชันของ commandline นั้นไม่จำเป็นต้องมีการขึ้นบรรทัดใหม่พิเศษดังนั้นโค้ดจะนับเป็น 28 ไบต์:

perl -lpe 's/\d/$"x$&/eg;s/|/|/g;y;/;' <<< "r1bk3r/p2pBpNp/n4n2/1p1NP2P/6P1/3P4/P1P1K3/q5b1"

1
หายไปใช่ไหม
user253751

15

เรติน่า 13 ไบต์

\d
$* 
/
¶

|

ลองออนไลน์!

คำอธิบาย

ส่วนแรก (สังเกตพื้นที่ต่อท้าย):

\d
$* 

คือการแปลงจำนวนช่องว่างที่เฉพาะเจาะจง Retina มี$*คุณสมบัติในการทำซ้ำ วิธีการทำงานคือ: <num>$*<char>ถ้าไม่มี<num>, Retina จะสมมติ$&หรือสตริงที่ตรงกันในกรณีนี้หมายเลขที่ตรงกัน

ส่วนต่อไป:

/
¶

มันค่อนข้างง่ายมันแทนที่ทั้งหมด/ด้วยซึ่งเป็นบรรทัดใหม่

ส่วนสุดท้ายทำงานเหมือนกัน:

    
|

นี้จะแทนที่ทุกอย่าง |(เพราะฉะนั้นทำไมมีอะไรในบรรทัดแรก) วาง|ทุกที่


1
คุณสามารถทำได้ทั้งหมดใน ASCII สำหรับจำนวนไบต์เดียวกันกับS`/ขั้นตอนที่สอง
Martin Ender

12

Ruby - 75 82 78 76 75 62 59 58 57 56 ไบต์

->n{"|#{n.gsub(/\d|
/){' '*$&.hex}.chars*?|}|".tr'/',$/}

บันทึกสองสามไบต์ต้องขอบคุณ Ventero

ให้ฉันอธิบาย ( \nแทนที่บรรทัดใหม่ตามตัวอักษร):

->n{"...".tr'/',$/}

สิ่งนี้ส่งคืนค่าของสตริงโดยปริยายซึ่ง/แทนที่ด้วย newline (โดยค่าเริ่มต้นจะ$/มี newline)

"|#{...}|"

มันง่ายมาก มันเป็นเพียงแค่สตริงที่มีไพพ์การแก้ไขสตริงและไพพ์อื่น การประมาณค่าสตริงจะถูกประเมิน

n.gsub(/\d|\n/){' '*$&.hex}...

สิ่งนี้จะแทนที่ทุกหมายเลขด้วยช่องว่างจำนวนมาก ฉันสามารถบันทึกไม่กี่ไบต์โดยหาบรรทัดใหม่ที่นี่; เพราะhexผลตอบแทน 0 ถ้าสตริงเป็นตัวเลขที่ไม่ถูกต้องเมื่อมันพบการขึ้นบรรทัดใหม่ - คือหนึ่งในตอนท้ายของผลมาจากการที่gets- มันแทนที่มันด้วยสตริง 0 ความยาวได้อย่างมีประสิทธิภาพลบ หากปราศจากสิ่งนี้จะมีท่อต่อท้าย

$&|d|เป็นตัวแปรมายากลซึ่งหมายถึงข้อความที่สมบูรณ์ของการจับคู่ตัวแปรใหม่ล่าสุดที่ช่วยให้ผมประหยัดไบต์โดยการกำจัด ฉันสามารถบันทึกไบต์อื่นโดยใช้.hexแทน.to_iซึ่งใช้ได้เพราะทุกหมายเลขน้อยกว่า 9 ซึ่งหมายความว่าเลขฐานสิบหกและทศนิยมมีค่าเท่ากัน

.chars*?|

สิ่งนี้ทำให้ไพพ์ระหว่างตัวละครทุกตัว โปรดทราบว่านี่คือสิ่งที่ทำให้ไพพ์บนทั้งสองด้านของบรรทัด (ยกเว้นอันแรกและสุดท้าย) เนื่องจากเครื่องหมายทับซึ่งท้ายที่สุดจะเปลี่ยนเป็นบรรทัดใหม่ผ่านtrนับเป็นอักขระและถูกล้อมรอบด้วยไพพ์ ?|เพียงหมายถึง "สตริงหนึ่งตัวอักษร"|""

และ ... แค่นั้นแหละ มันเป็นโปรแกรมที่เรียบง่ายอย่างตรงไปตรงมา มันใช้อุบายทางไวยากรณ์ที่ลับๆล่อๆมากมาย


2
คุณสามารถบันทึกตัวละครได้อีก 4 ตัวโดยใช้เทคนิคง่ายๆสองสามตัว: puts"|#{gets.gsub(/\d|\n/){' '*$&.hex}.chars*?|}|".split'/'(แน่นอนแทนที่\nด้วยตัวอักษรขึ้นบรรทัดใหม่อีกครั้ง)
Ventero

5

Pyth - 24 22 21 ไบต์

.i*\|72jcu:G`H*Hd9z\/

Test Suite

+                     Concatenate
 K\|                  Store "|" in K and use value
+         K           Concatenate to end
 jK                   Join string by K, this puts "|" between each char
  :                   String substitution
        \/            Replace "/"
         b            With newline
   u                  Reduce
        9             Over [0, 9)
         z            With input as base case
    :G                String substitution current val
     `H               Replace stringifyed int from list we're looping through
     *Hd              With " "*that int


4

JavaScript ES7, 80 70 ไบต์

เป็นฟังก์ชั่นนิรนามที่ยอมรับสตริงเป็นอินพุต

a=>[,...[+t?" ".repeat(t):t<"0"?`
`:t for(t of a)].join``,`
`].join`|`

80-byte ES6- ประเมินค่าอย่างเดียว

a=>a.split`/`.map(x=>[,...x.replace(/\d/g,t=>" ".repeat(t)),`
`].join`|`).join``

คำอธิบาย

เราใช้ความเข้าใจของอาเรย์เพื่อวนรอบรายการ:

[+t?" ".repeat(t):t<"0"?`
`:t for(t of a)]

นี่เทียบเท่ากับ:

[!isNaN(parseInt(t, 10)) ? " ".repeat(parseInt(t, 10)) : t === "/" ? "\n" : t for(t of a)]

ถ้าเป็นตัวเลขเรามีจำนวนช่องว่างนั้น ถ้าเป็น/เรามีการขึ้นบรรทัดใหม่ มิฉะนั้นเรามีตัวละคร จากนั้นเราเข้าร่วมกับความเข้าใจโดยไม่มีอะไรทำสตริง

จากนั้นเราจะสร้างอาร์เรย์ของความยาว [,...that,"\n"]3 ...แบ่งความเข้าใจเข้าร่วมเป็นตัวอักษร การเข้าร่วมสิ่งนี้ทำให้ได้ผลลัพธ์


คุณไม่ได้หมายถึง ES6 หรือ ES7 ยังไม่ออก แต่ผมเชื่อว่า
ericw31415

@ ericw31415 ไม่เป็นไรคุณถูกต้อง แต่เบราว์เซอร์บางตัวเริ่มนำส่วนหนึ่งของข้อมูลจำเพาะ ES7 มาใช้
Conor O'Brien

โอวตกลง. แต่ถึงกระนั้นรหัสของคุณไม่ได้ใช้คุณสมบัติ ES7 ใช่ไหม?
ericw31415

1
@ ericw31415 จริง ๆ แล้วมันทำ Array comprehensions ( [x for(x of a)]) คือ ES7
Conor O'Brien

ไม่ได้ถูกลบ Array Comprehensions ออกจากข้อมูลจำเพาะ MDN กล่าวว่าพวกเขาเป็น
MayorMonty

3

จูเลีย 62 ไบต์

s->split("|"join(replace(s,r"\d",d->" "^parse(d)),"|")"|","/")

นี่คือฟังก์ชั่นที่ไม่ระบุชื่อที่ยอมรับสตริงและส่งกลับอาร์เรย์ของสตริง หากต้องการเรียกใช้กำหนดค่าให้กับตัวแปร

วิธีการที่เป็นเช่นเดียวกับใน QPaysTaxes' ฉลาดทับทิมคำตอบ เราแทนที่แต่ละหลักในการป้อนข้อมูลที่มีช่องว่างที่หลายสถานที่|ระหว่างตัวละครแต่ละตัว, ตะปูลงบนด้านหน้าและด้านหลังและแยกออกเป็นอาร์เรย์บน|/

ลองออนไลน์!


Yay ฉันได้แรงบันดาลใจ: D
คดีของกองทุนโมนิกา

@QPaysTaxes คุณทำแน่นอน ทางออกที่ดี!
Alex A.


2

JavaScript (ES6), 69 67 62 ไบต์

s=>[,...s.replace(/[/-8]/g,c=>+c?' '.repeat(c):`
`),,].join`|`

เครื่องหมายจุลภาคพิเศษสร้างค่าว่างในการแยกด้านนอกซึ่งสร้าง|อักขระเริ่มต้นและอักขระสิ้นสุด คุณต้องใช้คอมม่าต่อท้ายสองตัวเนื่องจากคอมม่าต่อท้ายเป็นทางเลือกที่ท้ายรายการดังนั้นอันแรกยังคงเป็นส่วนหนึ่งของรายการก่อนหน้า

แก้ไข: บันทึก 5 ไบต์ด้วย @ user81655


จะ/[\d/]/g,c=>+c?` `.repeat(c):`\n`ทำงานอย่างไร
user81655

1
@ user81655 ขอบคุณ แต่ฉันไม่ชอบอิโมติคอนของคุณดังนั้นฉันจึงแทนที่ด้วยใบหน้าที่รำคาญด้วยแว่นตา
Neil

1

เรติน่า50 50ไบต์

สนุกมากฮ่าฮ่า ไม่เพียง แต่ฉันเป็น noob ใน Retina เท่านั้น แต่ยังอยู่ใน regex โดยทั่วไป ... นี่อาจเป็นเรื่องที่เล่นกอล์ฟได้มากดังนั้นฉันจะทำการวิจัยเพิ่มเติม

รหัส:

8
44
7
34
6
42
5
 4
4
22
3
 2
2

1

/
¶

|

ลองออนไลน์!


ลองใช้$*ฟังก์ชั่น :)
รั่วนูน



1

C, 252 ไบต์

i=-1,j,s=1,x;C(char*n){while(n[++i])s+=isdigit(n[i])?n[i]*2+1:2;char*m=(char*)malloc(s);for(i=j=-1;n[++i]&(m[++j]='|');)if(n[i]=='/')m[++j]='\n';else if(isdigit(n[i]))for(x=n[i]-'0';x;--x&&(m[++j]='|'))m[++j]=' ';else m[++j]=n[i];m[++j]='\0';return m;}

ลองรายละเอียดออนไลน์

// input-string, input-string-size
char* C(char*n)
{
    int i=-1,j,s=1,x;

    // figure out required grid size
    while(n[++i])s+=isdigit(n[i])?n[i]*2+1:2;
    char*m=(char*)malloc(s);

    i=j=-1;
    while(n[++i]) // while not end of string
    {
        m[++j]='|'; // seperator

        if (n[i]=='/') // end of row
            m[++j]='\n';
        else if (isdigit(n[i])) // fill spaces
            for(x=n[i]-'0';x;--x&&(m[++j]='|')) m[++j]=' ';
        else
            m[++j]=n[i]; // single literals
    }

    m[++j]='|';
    m[++j]='\0';
    return m;
}

1

JavaScript (FireFox 30+), 61

การใช้ความเข้าใจอาร์เรย์ที่ไม่ใช่ EcmaScript มาตรฐานอีกต่อไป

f=>'|'+[for(c of f)+c?' |'.repeat(c):c<'A'?`
|`:c+'|'].join``

ทดสอบ

F=f=>'|'+[for(c of f)+c?' |'.repeat(c):c<'A'?`\n|`:c+'|'].join``

console.log=x=>O.textContent+=x+'\n'

;['8/8/8/8/8/8/8/8','rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',
'r1bk3r/p2pBpNp/n4n2/1p1NP2P/6P1/3P4/P1P1K3/q5b1']
.forEach(t=>console.log(F(t)+'\n'))
<pre id=O></pre>


1

Lua, 106 ไบต์

print("|"..(...):gsub(".",function(c)return c:find("%d")and(" |"):rep(c)or c=="/"and"\n|"or c.."|"end),'')

Ungolfed

print("|"..                   -- prepend | to the following string
  (...):gsub(".",function(c)  -- iterate over each character in the argument
    return                    -- replaces in the argument
           c:find("%d")       -- if c is a number
             and(" |"):rep(c) --   replace by " |"*c
           or c=="/"          -- elseif c is a slash
             and"\n|"         -- replace by "\n|"
           or c.."|"          -- else (case letter)replace by c
  end)                        -- return the modified string
,'')                          -- add an empty parameter to print
                              -- it suppresses the second output of gsub

print((...):gsub(".",function(c)return(c:find("%d")and("| "):rep(c)or c=="/"and"|\n"or"|"..c)end).."|")
Leun Nun

print((...):gsub("%d",function(c)return("| "):rep(c)end):gsub("/","|\n"):gsub("([^%d%s|])","|%1").."|")สำหรับจำนวนไบต์เดียวกัน
Leun Nun

print((...):gsub("%d",function(c)return("| "):rep(c)end):gsub("([^%d |])","|%1"):gsub("/","\n").."|")คือ 101 ไบต์
Leun Nun

1

R (ออกจากการแข่งขัน)

ขออภัยถ้ามันไม่เหมาะสมที่จะโพสต์นี้ แต่ฉันคิดว่ามันเจ๋งที่ฉันเพิ่งเกิดขึ้นที่จะมีฟังก์ชั่นนอนรอบ ๆ ที่ใช้งานได้จริงสำหรับคำถามนี้โดยไม่ต้องแก้ไข! มันพิมพ์เอาต์พุต unicode มากกว่า ascii ฉันจำไม่ได้เลยว่าทำไมฉันถึงเขียน แต่มันก็ไม่ใช่คำตอบที่ท้าทาย

function(x){
# x = FEN position, a string
# can be split with / or ,
# example: forsythe("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R")

allowed <- c(paste(1:64), 
c("k", "q", "r", "b", "n", "p", "K", "Q", "R", "B", "N", "P"))
chars <- strsplit(x, "")[[1]]
chars <- chars[-which(!(chars %in% allowed))]
out <- c()
for (i in 1:length(chars)){
  if (chars[i] %in% paste(1:64)){
    out <- c(out, rep(" ", as.numeric(chars[i])))
  }
  else{
    out <- c(out, chars[i])
  }
}
if (length(out) < 64) out <- c(out, rep(" ", 64-length(out)))

pieces <- strsplit("KQRBNPkqrbnp", "")[[1]]
unicode <- c("\u2654", "\u2655", "\u2656", 
"\u2657", "\u2658", "\u2659", "\u265A", "\u265B", 
"\u265C", "\u265D", "\u265E", "\u265F")

for (i in 1:64){
  if (out[i] %in% pieces){
    out[i] <- unicode[which(pieces==out[i])]
  }
  else{
  }
}
out <- matrix(out, nc=8, byrow=T)
#print(out)

plot(0, xlim=c(0, 8), ylim=c(0, 8), type="n", xaxt="n", yaxt="n",
xlab="", ylab="")
for (i in 0:7){ for (j in 0:7){ rect(i, j, i+1, j+1,
col=ifelse(((i+j) %% 2) == 0, grey(0.95), "white"), border=F) }}

for (i in 0:7){ for (j in 0:7){
  text(i+0.5, j+0.5, out[8-j, i+1], cex=2)  
}}

axis(1, labels=letters[1:8], at=1:8 - 0.5, tick=F)
axis(2, labels=paste(1:8), at=1:8-0.5, las=2, tick=F)

}

กฎที่ระบุไว้ในศูนย์ช่วยเหลือของเราระบุว่าการแก้ปัญหาทั้งหมดจะต้องเป็นคู่แข่งที่ร้ายแรงสำหรับเกณฑ์การชนะที่ใช้งาน สำหรับ code golf นั่นหมายความว่าคำตอบทั้งหมดจะต้อง golfed
เดนนิส

ในทางเทคนิคมันเป็นกอล์ฟ แค่ไม่ค่อยดี
Flounderer

0

Haskell, 110 ไบต์

p '/'="\n"
p c|'1'<=c&&c<='8'=replicate(read[c])' '
p c=[c]
main=getLine>>=putStrLn.('|':).(>>=(:"|")).(>>=p)

Ungolfed:

p c | c=='/'           = "\n"
    | '1'<=c && c<='8' = replicate (read [c]) ' '
    | otherwise        = [c]
addPipes string = "|" ++ concatMap (\c -> [c] ++ "|") string
main = getLine >>= putStrLn . addPipes . concatMap p

0

Java 7, 190 184 ไบต์

String Z(int c){String m="";if(c==47)m+="|\n";else if(c>57)m+="|"+c;else while(c-->48)m+="| ";return m;}String C(String n){String m="";for(char x:n.toCharArray())m+=Z(x);return m+"|";}

ลองรายละเอียดออนไลน์

public static String Z(char c)
{
    String m="";
    if(c=='/')m+="|\n";
    else if(c>'9')m+="|"+c;
    else while(c-->'0')m+="| ";
    return m;
}

public static String C(String n)
{
    String m="";
    for(char x:n.toCharArray())m+=Z(x);
    return m+"|";
}

คุณสามารถบันทึกได้สองสามไบต์โดยใช้จำนวนเต็มแทนที่จะเป็นตัวอักษรถ่านในการเปรียบเทียบ
Blue

@ บันทึกย่อสีน้ำเงิน
Khaled.K


0

Python ขนาด 84 ไบต์

lambda a:"".join(c*c.isalpha()or"\n"*(c=="/")or" "*int(c)for c in a).replace("","|")

คำอธิบาย:

        c*c.isalpha()                                                       - if c is alphabetical, use c
                       "\n"*(c=="/")                                        - if it's "|", replace it with a newline
                                      " "*int(c)                            - else its an int.
"".join(                                                  ).replace("","|") - interweave "|" between the chars

0

> <>, 64 ไบต์

<v?(0:i
r\
"<o-*=@"%/":   v?*(@)@":/"::;?(0:o"|
 ^~?="0":-1o" "<

4 ไบต์ที่สูญเสียไปเนื่องจากปัญหาการจัดตำแหน่งไม่แน่ใจว่าจะนำมันออกมาได้อย่างไร ¯ \ _ (ツ) _ / ¯

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