Điều hướng thành công một trường tiểu hành tinh


36

Giới thiệu

Mọi người đều biết rằng khả năng điều hướng thành công một trường tiểu hành tinh là khoảng 3.720 đến 1. Nhưng bất chấp cảnh báo của bạn, Han Solo vẫn sẵn sàng thử vận ​​may.

Lo sợ cho cuộc sống nhân tạo của mình, bạn quyết định viết mã, theo phương ngữ đặc biệt của con tàu ( đọc: ngôn ngữ Code Golf ưa thích của bạn ), một chương trình tránh tiểu hành tinh sẽ quyết định con đường nào đi vào mê cung ASCII của tiểu hành tinh.

Đầu vào

Falcon Millenium có chương trình lập bản đồ trường tiểu hành tinh, cung cấp dữ liệu tương tự như sau:

|   #####           #########  |
| ######  #          ###   #   |
|   # #  #  #  ####   #        |
@              ##    ####       
|#   #   #       ###   ##      |
|##      ##      ####   #  #   |
|####           ##### #   ##   |

Các hàng trên cùng bên trái của Falcon, các hàng dưới cùng bên phải của Falcon và các cột thể hiện những gì ở phía trước con tàu.

  • Mỗi người #là một trở ngại.
  • Mỗi không gian là không gian trống mà con tàu có thể bay vào.
  • Đầu vào luôn cao 7 ký tự. Đây là giới hạn chiều rộng ánh xạ tiểu hành tinh.
  • Đầu vào luôn dài 32 ký tự (30 cho chính trường và 2 cho giới hạn bắt đầu và kết thúc). Đây là giới hạn độ sâu ánh xạ tiểu hành tinh. Thanh dọc |đánh dấu điểm bắt đầu và kết thúc của ánh xạ.
  • @là chim ưng. Nó luôn ở hàng giữa (hàng thứ 4) và cột đầu tiên trong đầu vào.
  • Khoảng trống còn lại trong các thanh dọc trên cột cuối cùng là nơi tàu phải đến. Nó luôn ở hàng giữa (hàng thứ 4) và cột cuối cùng trong đầu vào.

Đầu vào có thể được lấy dưới dạng chuỗi nhiều dòng, một chuỗi các chuỗi, từ STDIN hoặc tham số hàm hoặc đọc từ tệp.

Khả năng cơ động

Bạn được TIE-Fighters theo đuổi, do đó bạn phải luôn tiến về phía trước. Do đó, có ba cách con tàu có thể bay ở mỗi bước:

  • - Phía trước

  • / Chuyển tiếp và rẽ trái

  • \ Chuyển tiếp và rẽ phải

Ví dụ: đây là những đường dẫn hợp lệ:

@---

  --
 /  \ /
@    -

   -
  / \
 /   \
@     \

Như bạn có thể thấy, luôn có chính xác một lần di chuyển trên mỗi cột. Falcon là một mảnh rác, do đó nó không thể thực hiện những bước ngoặt dữ dội. Có nghĩa là di chuyển như /\hoặc không\/ được phép . Phải có ít nhất một chuyển tiếp thuần túy -giữa hai lượt đối diện. Mặt khác, xoay một chiều cho nhiều bước liên tiếp là có thể, như đã thấy ở trên.

Chiếc Falcon gặp sự cố nếu một người di chuyển khiến con tàu rơi vào vị trí có chướng ngại vật. Ví dụ: những di chuyển này dẫn đến sự cố:

@-#

@
 \
  #

  #
 /
@

Lưu ý rằng đây không phải là một sự cố:

@-#
  \
   -

Đầu ra

Bạn phải xuất cùng trường tiểu hành tinh ASCII, với đường dẫn hợp lệ đến cuối. Falcon phải được in ở điểm cuối thay vì điểm bắt đầu.

Ví dụ: một đầu ra hợp lệ cho ví dụ đầu vào được đưa ra trước đó sẽ là:

|   #####           #########  |
| ######  #--------  ###   #   |
|   # #  #/ #  ####\  #        |
 ---------      ##  \ #### ----@
|#   #   #       ### \ ## /    |
|##      ##      #### \ #/ #   |
|####           ##### #-- ##   |

Con đường của bạn chỉ cần không sụp đổ chim ưng. Nó không cần phải là con đường ngắn nhất có thể.

Bạn có thể cho rằng sẽ luôn có ít nhất một con đường có thể đi đến cuối cùng.

Bạn có thể xuất ra STDOUT, trong một tệp hoặc bất kỳ tương đương nào miễn là trường tiểu hành tinh được in chính xác như trong bài đăng này (ví dụ: xuất ra danh sách tọa độ cho đường dẫn không hợp lệ).

Các trường hợp thử nghiệm

  • Một trường tiểu hành tinh bình thường

    |   #####           #########  |
    | ######  #          ###   #   |
    |   # #  #  #  ####   #        |
    @              ##    ####       
    |#   #   #       ###   ##      |
    |##      ##      ####   #  #   |
    |####           ##### #   ##   |
    

    Sản lượng có thể

    |   #####           #########  |
    | ######  #--------  ###   #   |
    |   # #  #/ #  ####\  #        |
     ---------      ##  \ #### ----@
    |#   #   #       ### \ ## /    |
    |##      ##      #### \ #/ #   |
    |####           ##### #-- ##   |
    
  • Trường tiểu hành tinh không đều

    |# # # # # # # # # # # # # # # |
    | # # # # # # # # # # # # # # #|
    |# # # # # # # # # # # # # # # |
    @ # # # # # # # # # # # # # #   
    |# # # # # # # # # # # # # # # |
    | # # # # # # # # # # # # # # #|
    |# # # # # # # # # # # # # # # |
    

    Sản lượng có thể

    |# # # # # # # # # # # # # # # |
    | # # # # # # # # # # # # # # #|
    |# # # # # # # # # # # # # # # |
     -# #-# #-# #-# #-# #-# #-# #--@
    |#\#/#\#/#\#/#\#/#\#/#\#/#\#/# |
    | #-# #-# #-# #-# #-# #-# #-# #|
    |# # # # # # # # # # # # # # # |
    
  • Lõi tử thần

    |    #    #    #         #     |
    |         #    #    #          |
    |    #    #    #    #    #     |
    @    #    #    #    #    #      
    |    #    #         #    #     |
    |    #    #    #    #    #     |
    |    #         #    #    #     |
    

    Sản lượng có thể

    |    #    #    #   --    #     |
    |  ---    #    #  / #\   -     |
    | /  #\   #    # /  # \ /#\    |
     -   # \  #    #/   #  - # ----@
    |    #  \ # ----    #    #     |
    |    #   \#/   #    #    #     |
    |    #    -    #    #    #     |
    
  • Cái chết sao

    |##############################|
    |##############################|
    |##############################|
    @                               
    |##############################|
    |##############################|
    |##############################|
    

    Đầu ra

    |##############################|
    |##############################|
    |##############################|
     ------------------------------@
    |##############################|
    |##############################|
    |##############################|
    
  • Hang động tiểu hành tinh

    |### ##########################|
    |## # ############### ## ######|
    |# ###  ######## ### ## # #####|
    @ ###### ###### ### ## ###      
    |########  ### ### ## #########|
    |########## # ### ## ##########|
    |###########              #####|
    

    Sản lượng có thể

    |###-##########################|
    |##/#\############### ##-######|
    |#/###--######## ### ##/#\#####|
     -######\###### ### ##/###-----@
    |########--### ### ##/#########|
    |##########\# ### ##/##########|
    |###########--------      #####|
    

Chấm điểm

R2D2 đang bận bơi trong đầm lầy, do đó bạn sẽ phải tự lập trình bộ điều khiển của Falcon, thật tẻ nhạt. Do đó mã ngắn nhất sẽ thắng .


@DJMcMayhem: Về mặt kỹ thuật, dòng đầu tiên là "Giới thiệu";)
Alex A.

2
Tôi có thể hiểu được nó trông như thế nào dựa trên các ví dụ, nhưng việc mã hóa các bước di chuyển vẫn hơi khó hiểu với tôi. Nếu bạn xem ví dụ ở ví dụ "siêu thường xuyên", ngoại trừ di chuyển đầu tiên / cuối cùng, bạn luôn di chuyển theo đường chéo. Tuy nhiên, nó có -trong đường dẫn ở mỗi lượt, được định nghĩa là di chuyển "về phía trước". Nhưng các bước di chuyển thực tế luôn là hai đường chéo-trái theo sau là hai đường chéo-phải.
Reto Koradi 8/07/2015

@RetoKoradi Tôi có thể hiểu rằng đó không phải là tất cả rõ ràng nhưng ý tưởng cơ bản là tất cả các chuyển động làm cho bạn di chuyển chiều rộng của một nhân vật sang phải. Đường dẫn phải nhìn liên tục, đó là lý do tại sao di chuyển trước / tiếp theo sau rẽ phải và trái là một dòng trên / dưới dòng trước.
Gây tử vong

@apsillers Cả hai đều hợp lệ, nếu tôi hiểu bạn chính xác thì câu trả lời của bạn sẽ tốt
Fatalize 8/07/2015

Câu trả lời:


11

JavaScript (ES6), 186 201

f=([...s])=>(g=(i,l,c=k=" ")=>s[i]!=k&&s[i]!="@"?0:(i-130)?(s[i]=c,([..."/-\\"].some((c,j)=>!((--j&l&&j!=l)||!g(i+33*(l||j)+1,j,c)))||!(s[i]=k))):(s[i]="@",!console.log(s.join(""))))(99)

Đoạn trích có thể chạy được:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><textarea cols="33" rows="7" id="t"></textarea><br><button><b>Solve &gt;&gt;&gt;</b></button><hr><button id="1">Normal</button> <button id="2">Hyperregular</button> <button id="3">Death Star core</button> <button id="4">Death Star trench</button> <button id="5">Asteroid cave</button><script>f=(function($__0){var $__2,$__3,$__4,$__5;s = $__4 = $__0.split("");return (g = (function(i, l) {var c = arguments[2] !== (void 0) ? arguments[2] : k = " ";return s[i] != k && s[i] != "@" ? 0 : (i - 130) ? (s[i] = c, ("/-\\".split("").some((function(c, j) {return !((--j & l && j != l) || !g(i + 33 * (l || j) + 1, j, c));})) || !(s[i] = k))) : (s[i] = "@",$("#t").val(s.join("")));}))(99);});$("button").click(function() {this.id?$("#t").val(inputs[this.id]):f($("#t").val());});inputs = [,`|   #####           #########  |\n| ######  #          ###   #   |\n|   # #  #  #  ####   #        |\n@              ##    ####       \n|#   #   #       ###   ##      |\n|##      ##      ####   #  #   |\n|####           ##### #   ##   |`,`|# # # # # # # # # # # # # # # |\n| # # # # # # # # # # # # # # #|\n|# # # # # # # # # # # # # # # |\n@ # # # # # # # # # # # # # #   \n|# # # # # # # # # # # # # # # |\n| # # # # # # # # # # # # # # #|\n|# # # # # # # # # # # # # # # |`,`|    #    #    #         #     |\n|         #    #    #          |\n|    #    #    #    #    #     |\n@    #    #    #    #    #      \n|    #    #         #    #     |\n|    #    #    #    #    #     |\n|    #         #    #    #     |`,`|##############################|\n|##############################|\n|##############################|\n@                               \n|##############################|\n|##############################|\n|##############################|`,`|### ##########################|\n|## # ############### ## ######|\n|# ###  ######## ### ## # #####|\n@ ###### ###### ### ## ###      \n|########  ### ### ## #########|\n|########## # ### ## ##########|\n|###########              #####|`];$("#t").val(inputs[1]);</script

Hàm này chấp nhận một chuỗi đơn với dòng mới. Hàm chia chuỗi thành một mảng bằng ...toán tử và lấy chỉ mục cho (x,y)tọa độ theo (33 * y) + x.

Hàm chạy đệ quy, kiểm tra các bước di chuyển khác nhau có thể cho từng không gian. Khi gặp một chướng ngại vật, nó trả về giá trị giả và khi đạt đến không gian mục tiêu cuối cùng, nó sẽ trả về true. (Trong mã đánh gôn, mã này trueđược tạo bởi !console.log(...).)

Lưu ý rằng mã này không sử dụng các bước di chuyển rẽ phải dài, nhưng chấm dứt chúng bằng các động tác thẳng. Đó là, nó thực hiện tùy chọn thứ hai bên dưới, không phải tùy chọn thứ nhất:

\                       \
 \   (<= not this!)      -   (<= yes this!)
  \                       \

Điều này có vẻ là hợp pháp, vì -có thể hợp pháp đến trước hoặc sau một lượt, vậy tại sao không phải cả hai cùng một lúc? Điều đó trông đặc biệt kỳ lạ ở cuối, khi di chuyển cuối cùng \nhưng được hiển thị dưới dạng@ :

|  --#    #    #   ------#  -  |
| /  \    #    #  / #    \ / \ |
|/   #-   #    # /  #    #-   -|
     # \  #    #/   #    #     @
|    #  - # ----    #    #     |
|    #   \#/   #    #    #     |
|    #    -    #    #    #     |

Hack golf khó chịu yêu thích của tôi ở đây là lạm dụng đối số mặc định với c=k=" ". Các đối số (i,l,c=" ")sẽ nói "sử dụng chuỗi " "cho cnếu fkhông được cung cấp đối số thứ ba". Tuy nhiên, bằng cách thực hiện c=k=" ", chúng tôi nói "nếu ckhông được cung cấp, hãy lưu trữ " "trong biến toàn cục kvà sau đó lưu giá trị đó vàoc ". Vì đệ quy bắt đầu chỉ bằng một đối số duy nhất, kluôn được khởi tạo ở lệnh gọi hàm đầu tiên.

Nhẹ nhàng vô duyên:

// `i` - index in the string we're working on
// `l` - move character for this space (`/`, `\`, or `-`)
search = (i,l,c)=>{

  // not an open space; nip this recursive branch
  if(s[i]!=" "&&s[i]!="@") { return 0; }

  // we made it! the 130th space is (31,3)
  if(i==130) {
      s[i]="@";
      console.log(s.join(""));
      return true;
  }

  // fill in space with move character or a blank
  // (the space is only to blank out the initial `@`)
  s[i] = c || " ";

  // iterate through the 3 options and recursively explore the map
  return ['/','-','\\'].some((c,j)=>{
    --j;
    // if last move was sideways, and this is the opposite move, skip it
    if(l && j && j!=l) { return 0; }

    // recursively call search function on space pointed to by this move or the last move
    return search(i+33*(l||j)+1, j, c);
  })

  // if the `some` call is false (i.e. all options fail for this space)
  // then blank out this space and return false
  || !(s[i]=" ");

}

@ vihan1086 Đúng vậy, tôi hoàn toàn bỏ lỡ những khoảng trống đó khi chơi golf. D: Và chuyển từ một mảng sang một chuỗi phân chia cũng là một thay đổi tốt đẹp. Cảm ơn. :) Tôi cũng đã thực hiện một vài thay đổi khác (làm cho ký tự di chuyển hiện tại trở thành đối số thứ ba thay vì được xác định trong hàm và lưu trữ " "trong một biến) khiến điểm của tôi xuống thấp hơn.
apsillers

7

C (chương trình hoàn chỉnh), 249 247 235 byte

Đây là một chương trình hoàn chỉnh đọc đầu vào từ một tệp và xuất kết quả ra thiết bị xuất chuẩn. Tên của tệp được truyền dưới dạng tham số cho chương trình.

char f[7][33];g(i,j,c){return(i<0|i>6|f[i][j]%32?0:j<31?c%45-2?g(i,j+1,c)||g(i+1,j+1,92)||g(i-1,j+1,47):g(i+c/30-2,j+1,c)||g(i+c/30-2,j+1,45):1)?f[i][j]=j?j-31?c:64:32:0;}main(int p,char**v){read(open(v[1],0),f,231);g(3,0,45);puts(f);}

Ung dung:

/* the field */
char f[7][33];

/* i - row
 * j - col
 * c - movement
 */
g(i,j,c)
{
    return
            /* if we're in bounds and not on an obstacle */
            (i >= 0 & i<7 & f[i][j] % 32 == 0 ?
                    /* if we haven't reached the end */
                    j < 31 ?
                            /* are we going straight ahead? */
                            c%45-2 ?
                                    /* try to go straight */
                                    g(i,j+1,c)
                                    /* try to turn right */
                                    || g(i+1,j+1,92)
                                    /* try to turn left */
                                    || g(i-1,j+1,47)
                            /* turning */
                            :
                                    /* try to keep turning */
                                    g(i+c/30-2,j+1,c)
                                    /* try to go straight */
                                    || g(i+c/30-2,j+1,45)
                    /* done */
                    :1 /* replace this with c==45 to better represent the last move being a turn */
            /* invalid move, fail */
            :0)
            /* add the correct movement to the field */
            ? f[i][j] = j ? j - 31 ? c : 64 : 32
            /* no path, much sads :( */
            :0;
}

main(int p,char*v[])
{
    /* read input file */
    read(open(v[1],0),f,231);

    /* calculate the path */
    g(3,0,45);

    /* print it out */
    puts(f);
}

Đầu ra:

$ ./a.out test.inp
|   #####           #########  |
| ######  #          ###   #   |
|   # #  #  #  ####   #      --|
 ------------- ##----####   /  @
|#   #   #    \ /### \ ##  /   |
|##      ##    - #### \ # /#   |
|####           ##### #---##   |

$ ./a.out test2.inp
|# # # # #-# # # # # #-# # # # |
| # # # #/#\# # # # #/#\# # # #|
|# # # #/# #\# # # #/# #\# # # |
 -# # #/# # #\# # #/# # #\# #  @
|#\# #/# # # #\# #/# # # #\# #/|
| #\#/# # # # #\#/# # # # #\#/#|
|# #-# # # # # #-# # # # # #-# |

$ ./a.out test3.inp
|    #    #    #   ------#     |
|    -    #    #  / #    \     |
|   /#\   #    # /  #    #\    |
 --- # \  #    #/   #    # \   @
|    #  \ #    /    #    #  \ /|
|    #   \#   /#    #    #   - |
|    #    ---- #    #    #     |

$ ./a.out test4.inp
|##############################|
|##############################|
|##############################|
 ------------------------------@
|##############################|
|##############################|
|##############################|

$ ./a.out test5.inp
|###-##########################|
|##/#\############### ##-######|
|#/###--######## ### ##/#\#####|
 -######\###### ### ##/###-----@
|########--### ### ##/#########|
|##########\# ### ##/##########|
|###########--------      #####|

Có vẻ như bạn đã bỏ lỡ điểm cuối trong bài kiểm tra đầu tiên.
Reto Koradi

@RetoKoradi Đó là một -theo sau \, nhưng \được bao phủ bởi @. (Chương trình của tôi cũng làm điều tương tự.)
apsillers

1
@RetoKoradi Lặp đi lặp lại trước đó đã xử lý trường hợp đó tốt hơn. Đó là +4 byte. Tôi nhận thấy giải pháp của apsillers hoạt động tương tự vì vậy tôi đã chọn để tiết kiệm không gian.
Cole Cameron

Tôi hiểu rồi. Nó không phù hợp với tôi, nhưng tùy thuộc vào OP để quyết định những gì được cho phép. Tôi thấy rằng họ đã đưa ra một số tự do về cách di chuyển được thể hiện. Tôi muốn thấy một định nghĩa rõ ràng và độc đáo ngay từ đầu. Nó trông giống như một vấn đề thú vị, nhưng nó gần như không thú vị với sự mơ hồ.
Reto Koradi

3

Lisp thông thường, 303 byte

Có rất nhiều niềm vui với thử thách này, đó là nhiệm vụ codegolf đầu tiên tôi đã làm. Về cơ bản có một hàm đệ quy đơn giản, cố gắng mọi di chuyển khả thi cho đến khi đạt đến vị trí cuối.

Chơi gôn / Giảm thiểu

(let((s(open "i"))(n nil)(f(make-string 231)))(read-sequence f s)(labels((r(p s u d)(and(< 0 p 224)(find(aref f p)" @")(setf(aref f p)(cond((= 130 p)#\@)((or(unless d(r(- p 32)#\/ t n))(unless u(r(+ p 34)#\\ n t))(r(+ p(cond(u -32)(d 34)(t 1)))#\- n n))s)((return-from r)))))))(r 99 #\- n n)(princ f)))

Đọc đầu vào từ một tập tin i trong thư mục làm việc. Tôi khá chắc chắn rằng vẫn còn chỗ để cải thiện.

Mã đơn giản

(defun run-test (file)
  (let ((stream (open file)) ;;should use with-open-file for autoclose..
        (no nil) ;; alias for brevity
        (field (make-string 231)))
    (read-sequence field stream)
    (labels ((doit (pos sym going-up going-down)
               (and
                 (< 0 pos 224)
                 (find (aref field pos) " @")
                 (setf (aref field pos)
                       (cond
                         ((= 130 pos) #\@)
                         ((or
                            (unless going-down (doit (- pos 32) #\/ t no))
                            (unless going-up (doit (+ pos 34) #\\ no t))
                            (doit (+ pos (cond (going-up -32)
                                               (going-down 34)
                                               (t 1)))
                                  #\- no no))
                          sym)
                         ((return-from doit)))))))
      (doit 99 #\- no no)
      (princ field)
      nil)))

Sản lượng mẫu

|   #####       --  #########  |
| ######  #    /  \  ###   # - |
|   # #  #  # /####\  #     / \|
--   -       / ##   \####  /   @
|#\ /#\  #  /    ### \ ## /    |
|##-   \ ##/     #### \ #/ #   |
|####   ---     ##### #-- ##   |

|  --#    #    #   --    #-    |
| /  \    #    #  / #\   / \   |
|/   #\   #    # /  # \ /#  \  |
-    # \  #    #/   #  - #   \ @
|    #  \ # ----    #    #    -|
|    #   \#/   #    #    #     |
|    #    -    #    #    #     |

|# #-# # # # # #-# # # # # #-# |
| #/#\# # # # #/#\# # # # #/#\#|
|#/# #\# # # #/# #\# # # #/# #\|
--# # #\# # #/# # #\# # #/# #  @
|# # # #\# #/# # # #\# #/# # # |
| # # # #\#/# # # # #\#/# # # #|
|# # # # #-# # # # # #-# # # # |

2

ActionScript 3, 364 byte

Tôi chia điều này thành hai chức năng; một để thay đổi mảng thành một mảng các mảng và một để đệ quy để tính toán đường bay.

function m(f){for(var i=0;i<f.length;i++){f[i]=f[i].split("");}n(f,0,3,0);return f;}function n(f,x,y,m){var P=f[y][x],X=x+1,A=y-1,B=y,C=y+1,T=true,F=false,E='-';if (y<0||y>6||P=='#'||P=='|')return F;if (x==31){f[y][x]='@';return T;}if(m<0&&y>0){B=A;C=9;E='/';}else if(m>0&&y<6){A=9;B=C;E='\\';}if (n(f,X,B,0)||n(f,X,A,-1)||n(f,X,C,1)){f[y][x]=E;return T;return F;}

Phiên bản bị đánh cắp trong một chương trình với một trường tiểu hành tinh mẫu được xác định:

package
{
    import flash.display.Sprite;

    public class AsteroidNavigator extends Sprite
    {
        var field:Array;
        public function AsteroidNavigator()
        {
            field = [
"|   #####           #########  |",
"| ######  #          ###   #   |",
"|   # #  #  #  ####   #        |",
"@              ##    ####       ",
"|#   #   #       ###   ##      |",
"|##      ##      ####   #  #   |",
"|####           ##### #   ##   |"];
            m(field);
            printField();
        }

        function m(f){
            for(var i=0;i<f.length;i++){
                f[i]=f[i].split("");\
            }
            n(f,0,3,0);
            return f;
        }

        private function n(field,x,y,m) {
            var C = field[y][x];
            if (x > 31 || C == '#' || C == '|') {
                return false;
            }
            if (x == 31 && y == 3) {
                field[y][x] = '@';
                return true;
            }
            if (m == 0) {
                if (n(x+1, y, 0) || ((y>0) && n(x+1, y-1, -1)) || ((y<6) && n(x+1, y+1, 1))) {
                field[y][x] = '-';
                return true;
                }
            } else if ((m<0) && (y>0)) {
                if ((n(x+1, y-1, -1) || n(x+1, y-1, 0))) {
                    field[y][x] = '/';
                    return true;
                }
            } else if ((m>0) && (y<6)) {
                if ((n(x+1, y+1, 1) || n(x+1, y+1, 0))) {
                    field[y][x] = '\\';
                    return true;
                }
            }
            return false;
        }

        private function printField() {
            var sb = "";
            for each (var row:Array in field) {
                sb += row.join("") + "\n";
            }
            trace(sb);
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.