Đây là một sự thích nghi của Core War , một chương trình KOTH lập trình có từ thế kỷ 20. Để cụ thể hơn, nó đang sử dụng một bộ hướng dẫn cực kỳ đơn giản chủ yếu dựa trên đề xuất ban đầu .
Lý lịch
Trong Core War, có hai chương trình chiến đấu để kiểm soát máy tính. Mục tiêu của mỗi chương trình là giành chiến thắng bằng cách định vị và chấm dứt chương trình đối nghịch.
Trận chiến diễn ra trong bộ nhớ chính của máy tính. Bộ nhớ này được gọi là Core và chứa 8192 địa chỉ. Khi trận chiến bắt đầu, mã cho mỗi đối thủ (được gọi là chiến binh) được đặt trong một đoạn bộ nhớ ngẫu nhiên. Thực hiện chương trình xen kẽ giữa các chiến binh, thực hiện một chỉ dẫn của mỗi chiến binh. Mỗi hướng dẫn có khả năng sửa đổi một phần của Lõi, dẫn đến khả năng tự sửa đổi chương trình.
Mục tiêu là chấm dứt chương trình đối lập. Một chương trình chấm dứt khi nó cố thực hiện một lệnh không hợp lệ, đó là bất kỳ DAT
lệnh nào .
Bộ hướng dẫn
Mỗi chương trình bao gồm một loạt các hướng dẫn cấp thấp, mỗi hướng dẫn có hai trường, được gọi là trường A và B.
Bộ hướng dẫn này rút ra rất nhiều từ thông số ban đầu. Những thay đổi chính là 1) làm rõ về việc thêm / bớt các lệnh và 2) thay đổi #
chế độ địa chỉ để cho phép nó được sử dụng ở bất cứ đâu. Hầu hết các phiên bản đầy đủ của Core Wars có hơn 20 opcodes, 8 chế độ địa chỉ và một bộ "sửa đổi hướng dẫn".
Mã nguồn
Mỗi hướng dẫn phải có một trong bảy opcodes khác nhau.
DAT A B
- (dữ liệu) - Điều này chỉ đơn giản là giữ các sốA
vàB
. Điều quan trọng là, một quá trình sẽ chết khi nó cố thực hiện một lệnh DAT.MOV A B
- (di chuyển) - Thao tác này sẽ di chuyển nội dung của vị tríA
bộ nhớ sang vị trí bộ nhớB
. Đây là một minh chứng trước và sau:MOV 2 1 ADD @4 #5 JMP #1 -1
MOV 2 1 JMP #1 -1 JMP #1 -1
ADD A B
- (thêm) - Điều này thêm nội dung của vị tríA
bộ nhớ vào vị trí bộ nhớB
. Hai trường đầu tiên của cả hai được thêm vào và các trường thứ hai được thêm vào.ADD 2 1 MOV @4 #5 JMP #1 -1
ADD 2 1 MOV @5 #4 JMP #1 -1
SUB A B
- (trừ) - Điều này trừ nội dung của vị trí bộ nhớA
từ (và lưu kết quả vào) vị trí bộ nhớB
.SUB 2 1 MOV @4 #5 JMP #1 -1
SUB 2 1 MOV @3 #6 JMP #1 -1
JMP A B
- (nhảy) - Nhảy đến vị tríA
, sẽ được thực hiện chu kỳ tiếp theo.B
phải là một số nhưng không làm gì cả (bạn có thể sử dụng nó để lưu trữ thông tin).JMP 2 1337 ADD 1 2 ADD 2 3
Nhảy có nghĩa là
ADD 2 3
sẽ được thực hiện chu kỳ tiếp theo.JMZ A B
- (nhảy nếu không) - Nếu cả hai trường của dòngB
là 0, thì chương trình sẽ nhảy đến vị tríA
.JMZ 2 1 SUB 0 @0 DAT 23 45
Vì hai trường của lệnh 1 là 0, nên lệnh DAT sẽ được thực thi lần lượt tiếp theo, dẫn đến cái chết sắp xảy ra.
CMP A B
- (so sánh và bỏ qua nếu không bằng nhau) - Nếu các trường trong hướng dẫnA
vàB
không bằng nhau, hãy bỏ qua hướng dẫn tiếp theo.CMP #1 2 ADD 2 #3 SUB @2 3
Vì hai trường của hướng dẫn 1 và 2 có giá trị bằng nhau, nên lệnh ADD không bị bỏ qua và được thực hiện lần lượt tiếp theo.
Khi hai hướng dẫn được thêm / bớt, hai trường (A và B) sẽ được thêm / trừ theo cặp. Chế độ địa chỉ và opcode không được thay đổi.
Chế độ địa chỉ
Có ba loại chế độ địa chỉ. Mỗi trong hai trường của một lệnh có một trong ba chế độ địa chỉ này.
Ngay lập tức
#X
-X
là dòng được sử dụng trực tiếp trong tính toán. Ví dụ,#0
là dòng đầu tiên của chương trình. Các dòng tiêu cực đề cập đến các dòng trong lõi trước khi bắt đầu chương trình.... //just a space-filler ... ADD #3 #4 DAT 0 1 DAT 2 4
Điều này sẽ thêm dòng đầu tiên trong hai dòng DAT vào dòng thứ hai, vì các dòng này nằm trong dòng 3 và 4, tương ứng. Tuy nhiên, bạn sẽ không muốn sử dụng mã này, bởi vì DAT sẽ giết bot của bạn trong chu kỳ tiếp theo.
Tương đối
X
- SốX
đại diện cho vị trí của một địa chỉ bộ nhớ đích, liên quan đến địa chỉ hiện tại. Số tại vị trí này được sử dụng trong tính toán. Nếu dòng#35
đang được thực thi và chứa-5
, thì dòng#30
được sử dụng.... //just a space-filler ... ADD 2 1 DAT 0 1 DAT 2 4
Điều này sẽ thêm dòng DAT thứ hai vào đầu tiên.
Gián tiếp
@X
- SốX
đại diện cho một địa chỉ tương đối. Các nội dung tại vị trí đó được tạm thời thêm vào số X để tạo thành một địa chỉ tương đối mới, từ đó số được lấy. Nếu dòng#35
đang được thực thi, và trường thứ hai của nó là@4
, và trường thứ hai của dòng#39
chứa số-7
, thì dòng#32
được sử dụng.... //just a space-filler ... ADD @1 @1 DAT 0 1 DAT 2 4
Điều này sẽ thêm ĐẠT đầu tiên vào lần thứ hai, nhưng theo cách dễ hiểu hơn. Trường đầu tiên là @ 1, lấy dữ liệu từ địa chỉ tương đối đó, là trường đầu tiên của DAT đầu tiên, 0. Đây được hiểu là địa chỉ tương đối thứ hai từ vị trí đó, vì vậy 1 + 0 = 1 cho tổng số bù đắp từ hướng dẫn ban đầu. Đối với trường thứ hai, @ 1 nhận giá trị từ địa chỉ tương đối đó (số 1 trong trường thứ hai của ĐẠT thứ nhất) và thêm nó vào chính nó theo cùng một cách. Tổng bù là 1 + 1 = 2. Vì vậy, hướng dẫn này được thực hiện tương tự như
ADD 1 2
.
Mỗi chương trình có thể chứa tới 64 hướng dẫn.
Khi một vòng bắt đầu, hai chương trình được đặt ngẫu nhiên trong một ngân hàng bộ nhớ với 8192 vị trí. Con trỏ lệnh cho mỗi chương trình bắt đầu khi bắt đầu chương trình và được tăng lên sau mỗi chu kỳ thực hiện. Chương trình chết khi con trỏ lệnh của nó cố thực hiện một DAT
lệnh.
Các thông số của lõi
Kích thước lõi là 8192, với thời gian chờ là 8192 * 8 = 65536 tick. Cốt lõi là tuần hoàn, do đó, ghi vào địa chỉ 8195 cũng giống như viết vào địa chỉ 3. Tất cả các địa chỉ không sử dụng được khởi tạo DAT #0 #0
.
Mỗi đối thủ cạnh tranh không được dài hơn 64 dòng. Số nguyên sẽ được lưu dưới dạng số nguyên có chữ ký 32 bit.
Phân tích cú pháp
Để giúp lập trình dễ dàng hơn cho các đối thủ cạnh tranh, tôi sẽ thêm một tính năng nhãn dòng vào trình phân tích cú pháp. Bất kỳ từ nào xuất hiện trên một dòng trước opcode sẽ được hiểu là nhãn dòng. Ví dụ, tree mov 4 6
có nhãn dòng tree
. Nếu, bất cứ nơi nào trong chương trình, có một trường có chứa tree
#tree
hoặc @tree
, một số sẽ được thay thế. Ngoài ra, viết hoa được bỏ qua.
Dưới đây là một ví dụ về cách nhãn dòng được thay thế:
labelA add labelB @labelC
labelB add #labelC labelC
labelC sub labelA @labelB
Ở đây, các nhãn A, B và C nằm trên các dòng 0, 1 và 2. Các trường hợp #label
sẽ được thay thế bằng số dòng của nhãn. Các trường hợp label
hoặc @label
được thay thế bằng vị trí tương đối của nhãn. Chế độ địa chỉ được bảo tồn.
ADD 1 @2
ADD #2 1
SUB -2 @-1
Chấm điểm
Đối với mỗi cặp thí sinh, mọi trận chiến có thể được thực hiện. Vì kết quả của một trận chiến phụ thuộc vào độ lệch tương đối của hai chương trình, nên mọi sự bù đắp có thể (khoảng 8000 trong số chúng) đều được thử. Hơn nữa, mỗi chương trình có một cơ hội để di chuyển đầu tiên trong mỗi bù. Chương trình giành được phần lớn các khoản bù đắp này là người chiến thắng của cặp.
Đối với mỗi cặp mà một chiến binh chiến thắng, nó được thưởng 2 điểm. Đối với mỗi cà vạt, một chiến binh được thưởng 1 điểm.
Bạn được phép gửi nhiều hơn một chiến binh. Các quy tắc điển hình cho nhiều lần gửi được áp dụng, chẳng hạn như không gắn thẻ, không hợp tác, không làm vua, v.v ... Thực sự không có chỗ cho điều này trong dù sao trong Chiến tranh cốt lõi, vì vậy nó không phải là vấn đề lớn.
Bộ điều khiển
Mã cho bộ điều khiển, cùng với hai bot ví dụ dễ dàng, được đặt ở đây . Vì sự cạnh tranh này (khi chạy bằng cài đặt chính thức) là hoàn toàn quyết định, bảng xếp hạng bạn tạo sẽ giống hệt như bảng xếp hạng chính thức.
Bot ví dụ
Dưới đây là một ví dụ bot thể hiện một số tính năng của ngôn ngữ.
main mov bomb #-1
add @main main
jmp #main 0
bomb dat 0 -1
Bot này hoạt động bằng cách từ từ xóa tất cả bộ nhớ khác trong lõi bằng cách thay thế nó bằng một "quả bom". Vì bom là một DAT
chỉ dẫn, bất kỳ chương trình nào đạt được bom sẽ bị phá hủy.
Có hai nhãn dòng, "chính" và "bom" dùng để thay thế số. Sau khi tiền xử lý, chương trình trông như thế này:
MOV 3 #-1
ADD @-1 -1
JMP #0 0
DAT 0 -1
Dòng đầu tiên sao chép quả bom vào dòng ngay phía trên chương trình. Dòng tiếp theo thêm giá trị của bom ( 0 -1
) vào lệnh di chuyển và nó cũng thể hiện việc sử dụng @
chế độ địa chỉ. Sự bổ sung này làm cho lệnh di chuyển trỏ đến một mục tiêu mới. Lệnh tiếp theo vô điều kiện nhảy trở lại bắt đầu chương trình.
Bảng xếp hạng hiện tại
24 - Turbo
22 - DwarvenEngineer
20 - HanShotFirst
18 - Dwarf
14 - ScanBomber
10 - Paranoid
10 - FirstTimer
10 - Janitor
10
- Evolve
6 - EasterBunny 6 - CopyPasta
4 - Imp
2 - Slug
Kết quả theo cặp:
Dwarf > Imp
CopyPasta > Imp
Evolved > Imp
FirstTimer > Imp
Imp > Janitor
Imp > ScanBomber
Slug > Imp
DwarvenEngineer > Imp
HanShotFirst > Imp
Turbo > Imp
EasterBunny > Imp
Paranoid > Imp
Dwarf > CopyPasta
Dwarf > Evolved
Dwarf > FirstTimer
Dwarf > Janitor
Dwarf > ScanBomber
Dwarf > Slug
DwarvenEngineer > Dwarf
HanShotFirst > Dwarf
Turbo > Dwarf
Dwarf > EasterBunny
Dwarf > Paranoid
Evolved > CopyPasta
FirstTimer > CopyPasta
Janitor > CopyPasta
ScanBomber > CopyPasta
CopyPasta > Slug
DwarvenEngineer > CopyPasta
HanShotFirst > CopyPasta
Turbo > CopyPasta
CopyPasta > EasterBunny
Paranoid > CopyPasta
Evolved > FirstTimer
Evolved > Janitor
ScanBomber > Evolved
Evolved > Slug
DwarvenEngineer > Evolved
HanShotFirst > Evolved
Turbo > Evolved
EasterBunny > Evolved
Paranoid > Evolved
Janitor > FirstTimer
ScanBomber > FirstTimer
FirstTimer > Slug
DwarvenEngineer > FirstTimer
HanShotFirst > FirstTimer
Turbo > FirstTimer
FirstTimer > EasterBunny
FirstTimer > Paranoid
ScanBomber > Janitor
Janitor > Slug
DwarvenEngineer > Janitor
HanShotFirst > Janitor
Turbo > Janitor
Janitor > EasterBunny
Janitor > Paranoid
ScanBomber > Slug
DwarvenEngineer > ScanBomber
HanShotFirst > ScanBomber
Turbo > ScanBomber
ScanBomber > EasterBunny
ScanBomber > Paranoid
DwarvenEngineer > Slug
HanShotFirst > Slug
Turbo > Slug
EasterBunny > Slug
Paranoid > Slug
DwarvenEngineer > HanShotFirst
Turbo > DwarvenEngineer
DwarvenEngineer > EasterBunny
DwarvenEngineer > Paranoid
Turbo > HanShotFirst
HanShotFirst > EasterBunny
HanShotFirst > Paranoid
Turbo > EasterBunny
Turbo > Paranoid
Paranoid > EasterBunny
Bản cập nhật mới nhất (phiên bản mới của Turbo và Paranoid) mất khoảng 5 phút để chạy trên một máy tính xách tay cũ. Tôi muốn cảm ơn Ilmari Karonen vì những cải tiến của anh ấy đối với bộ điều khiển . Nếu bạn có một bản sao cục bộ của bộ điều khiển, bạn nên cập nhật các tệp của mình.