-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
model Redstone2L {
// ============================================================
// Redstone2L DSL: 2층(ground/top) 조합회로(PnR/합성)용 선언형 모델
// - "place" : 배치 변수(블록을 놓을지)
// - "state" : 상태 변수(시나리오별 전기적 상태: 파워/온오프)
// - "shape" : 모양/연결 같은 파생 구조(더스트 축, 연결 방향 등)
// - "sources": “어디서 파워가 들어오는가”를 집합으로 누적하는 개념(OR-원인 집합)
// - "rule" : 제약 묶음(ILP로 내려갈 논리식/등가식/함의/합 제약 등)
// ============================================================
// -------------------------
// 0) Index / Topology
// -------------------------
// 격자상의 한 칸을 (x,z)로 인덱싱한다. (y는 Layer로 분리)
index Cell = (x,z) in Grid;
// 2층 레이어: 바닥(GROUND)과 그 위 1칸(TOP)
enum Layer { GROUND=0, TOP=1 }
// 4방향
enum Dir { N,E,S,W }
// c에서 방향 d로 인접한 셀(격자 밖이면 null 가능)
fn neigh(c:Cell, d:Dir) -> Cell?;
// 벽토치가 붙을 "지지 블록" 위치를 계산
// (예: 벽토치가 c의 면 d에 붙으면, 실제로는 d방향 반대쪽 블록을 지지로 삼는 식)
fn supportForWallTorch(c:Cell, d:Dir) -> Cell?;
// 리피터 방향 d를 기준으로, 입력(back) 셀 위치
fn back(c:Cell, d:Dir) -> Cell?;
// 리피터 방향 d를 기준으로, 출력(front) 셀 위치
fn front(c:Cell, d:Dir) -> Cell?;
// -------------------------
// 1) 시나리오 / 핀
// -------------------------
// 시나리오(진리표 검증용): IN=0인 세계(s=0)와 IN=1인 세계(s=1)를 동시에 풀도록 복제
scenario Sc in {0,1};
// 외부에서 관찰/강제하는 관측 지점(입력/출력 핀)
pin IN : ObsPoint;
pin OUT : ObsPoint;
// -------------------------
// 2) 배치 변수 (place)
// -------------------------
// "place X[...] : Type" 는
// "그 위치에 해당 블록/부품을 둘지"를 나타내는 0/1 결정변수.
place S[Cell] : Solid; // 해당 Cell에 '고체 블록(솔리드 블록)'을 둘지
place D[Layer, Cell] : Dust; // 해당 Layer/Cell에 레드스톤 더스트를 둘지
place T_stand[Cell] : Torch; // 해당 Cell에 '스탠딩 토치(위에 올리는 토치)'를 둘지
place T_wall[Cell, Dir] : Torch;// Cell에 방향 Dir로 벽토치를 둘지(어느 면에 붙는지)
place R[Cell, Dir] : Repeater; // Cell에 방향 Dir로 리피터를 둘지
// -------------------------
// 3) 상태 변수 (state) - 시나리오별
// -------------------------
// "state"는 배치가 고정된 뒤, 시나리오 s마다 전기 상태(파워)를 나타냄.
// 보통 place가 1이어야만 state가 1이 될 수 있게 R_DOMAIN에서 묶는다.
state BP[Sc, Cell] : BlockPower; // 블록이 파워(간접파워 포함) 되었는지
state DP[Sc, Layer, Cell] : DustOn; // 더스트가 켜졌는지(신호를 띄는지)
state TO_stand[Sc, Cell] : TorchOn; // 스탠딩 토치가 ON인지
state TO_wall[Sc, Cell, Dir] : TorchOn; // 벽토치가 ON인지
state RO[Sc, Cell, Dir] : RepOn; // 리피터 출력이 ON인지(활성화) — “리피터가 켜짐”
// -------------------------
// 4) 모양/연결(Shape) 변수
// -------------------------
// 레드스톤 더스트는 “연결 후보”에 따라 모양(축/크로스)이 결정되고,
// 모양이 Conn에 반영되어 어떤 방향으로 전력이 전달되는지 컷(cutoff)된다.
shape Conn[Layer, Cell, Dir] : Conn; // 더스트가 해당 방향 Dir로 "연결되어 있음"을 의미
shape AxisNS[Layer, Cell] : Axis; // 더스트가 NS축 모양(남북 직선)
shape AxisEW[Layer, Cell] : Axis; // 더스트가 EW축 모양(동서 직선)
shape Cross[Layer, Cell] : Axis; // 더스트가 4방향 교차 모양(크로스) 허용 여부/선택
// -------------------------
// 5) Source-set(원인 집합) 변수
// -------------------------
// “이 셀이 파워를 받는 원인들이 뭐냐”를 원인 집합으로 누적하고,
// 나중에 OR{SrcSet} 형태로 "하나라도 있으면 켜짐"을 만든다.
// (백엔드에서는 결국 OR-선형화로 내려갈 것)
sources BlockSrc[Sc, Cell] : SrcSet; // 블록을 파워시키는 원인들의 집합
sources DustSrc[Sc, Layer, Cell] : SrcSet; // 더스트를 켜는 원인들의 집합
// -------------------------
// 6) 파생 헬퍼(derived helper) 선언
// -------------------------
// 네가 def로 쓰던 파생식들이 DSL에선 변수로 선언되어야 ILP로 내릴 수 있음.
shape Cand[Layer, Cell, Dir] : Bool; // 해당 방향으로 연결 후보가 있는지(더스트/주변 환경 기반)
shape DustPowersBlock[Sc, Layer, Cell, Dir] : Bool; // 더스트가 그 방향 이웃 블록을 파워하는지(Conn 컷 포함)
state RepIn[Sc, Cell, Dir] : Bool; // 리피터 입력이 들어왔는지(백 방향에서 신호가 들어오는지)
// ============================================================
// A) placement 제약
// ============================================================
rule R_PLACEMENT_BASE {
forall(c in Cell) {
// TOP 레이어에 더스트를 놓으려면, 그 아래(해당 Cell)에 솔리드 블록이 필요
// (마인크래프트에서 더스트는 블록 위에만 올려둘 수 있다는 규칙)
require D[TOP, c] -> S[c];
// 스탠딩 토치도 블록 위에만 올려둘 수 있으므로, 블록이 있어야 한다
require T_stand[c] -> S[c];
}
forall((c,d) in Cell * Dir) {
// 벽토치 배치 규칙:
// - 벽토치 자체는 "빈 공간" 위치(c)에 존재해야 하고(!S[c])
// - 지지 블록(supportForWallTorch(c,d))은 솔리드여야 한다
require T_wall[c,d] -> (!S[c] and S[supportForWallTorch(c,d)]);
}
forall(c in Cell) {
// 한 칸에는 벽토치를 최대 1개 방향만 달 수 있게 제한
require sum(d in Dir) T_wall[c,d] <= 1;
// 한 칸에는 리피터도 최대 1개 방향만 둘 수 있게 제한
require sum(d in Dir) R[c,d] <= 1;
}
feature PACKING_ONEHOT {
forall(c in Cell) {
// 패킹(배타) 제약:
// 동일 Cell(동일 좌표)에 "서로 겹쳐서" 놓이면 안 되는 것들을 한 번에 제한
// - ground 더스트 / top 더스트 / 스탠딩 토치 / 벽토치(4방 중 1) / 리피터(4방 중 1)
// - 합이 1을 넘지 않게 해서 한 칸에는 최대 하나만 두도록 강제
require D[GROUND,c] + D[TOP,c] + T_stand[c]
+ sum(d in Dir) T_wall[c,d]
+ sum(d in Dir) R[c,d] <= 1;
}
}
}
// ============================================================
// B) domain linking (state <= placement)
// ============================================================
rule R_DOMAIN {
forall(s in Sc) {
forall(c in Cell) {
// 상태 변수는 배치가 있어야만 켜질 수 있다(도메인 링크)
// 예: 블록파워 BP가 1이면 그 위치에 블록 S가 반드시 존재
require BP[s,c] -> S[c];
// 더스트 상태 DP가 1이면 해당 더스트가 배치돼 있어야 함
require DP[s,GROUND,c] -> D[GROUND,c];
require DP[s,TOP,c] -> D[TOP,c];
// 토치 ON 상태는 토치가 배치돼 있어야 함
require TO_stand[s,c] -> T_stand[c];
}
forall((c,d) in Cell * Dir) {
// 벽토치 ON 상태는 벽토치 배치가 있어야 함
require TO_wall[s,c,d] -> T_wall[c,d];
// 리피터 ON 상태는 리피터 배치가 있어야 함
require RO[s,c,d] -> R[c,d];
}
}
}
// ============================================================
// C) torch inverter (토치 인버터 동작)
// ============================================================
rule R_TORCH_INVERTER {
forall(s in Sc) {
forall(c in Cell) {
// 스탠딩 토치의 기본 동작:
// - 토치가 설치되어 있고(T_stand=1)
// - 붙어있는 블록이 파워되지 않았으면(!BP)
// => 토치는 ON
// 즉, 토치는 “부착 블록의 BP”를 반전한다(인버터)
def TO_stand[s,c] <-> (T_stand[c] and !BP[s,c]);
}
forall((c,d) in Cell * Dir) {
let sup = supportForWallTorch(c,d);
// 벽토치도 마찬가지로, 지지 블록(sup)의 BP를 반전한다
def TO_wall[s,c,d] <-> (T_wall[c,d] and !BP[s,sup]);
}
}
}
rule R_TORCH_ANTICHEAT_EXCLUDE_ATTACHED_BLOCK {
// 토치 규칙의 중요한 예외:
// "토치는 자신이 붙어있는 블록(attached/support)을 파워하지 않는다"
// 즉, 토치 출력이 BlockSrc에 들어가더라도 attached 블록으로는 들어가면 안 됨.
// 여기서는 sources 집합에서 특정 원인을 "exclude"로 빼는 형태로 표현.
forall(s in Sc) {
forall(c in Cell) {
// 스탠딩 토치 출력이 BlockSrc[s,c]에 (자동으로) 들어가는 상황이 있더라도,
// "자기 부착 블록(c)"에 대해서는 제외한다는 의미
exclude BlockSrc[s,c] += TorchOut(stand,c);
}
forall((c,d) in Cell * Dir) {
let sup = supportForWallTorch(c,d);
// 벽토치도 지지 블록(sup)에는 파워 원인으로 추가되지 못하게 exclude
exclude BlockSrc[s,sup] += TorchOut(wall,c,d);
}
}
}
// ============================================================
// D) repeater (리피터 스켈레톤)
// ============================================================
rule R_REPEATER {
forall(s in Sc) {
forall((c,d) in Cell * Dir) {
let b = back(c,d); // 입력 방향(뒤)
let f = front(c,d); // 출력 방향(앞)
// 리피터 입력 조건(대충의 OR):
// - back쪽 셀의 더스트가 켜져 있거나
// - back쪽 셀의 블록이 파워되어 있거나
// - back쪽 셀에 토치 등이 파워를 주고 있거나(추상 헬퍼)
// 실제 마크 리피터 입력 규칙을 더 정확히 모델링하려면 이 부분을 확장해야 함.
def RepIn[s,c,d] <-> OR{
DP[s,GROUND,b],
DP[s,TOP,b],
BP[s,b],
TorchPowersCell(s,b)
};
// 리피터 ON: 리피터가 설치되어 있고, 입력이 들어오면 출력 ON
def RO[s,c,d] <-> (R[c,d] and RepIn[s,c,d]);
// 리피터 출력은 앞쪽 셀의 더스트/블록을 파워 소스로 추가한다
// (DustSrc/BlockSrc에 원인으로 누적)
add DustSrc[s,GROUND,f] += RepOut(c,d);
add DustSrc[s,TOP,f] += RepOut(c,d);
add BlockSrc[s,f] += RepOut(c,d);
}
}
}
// ============================================================
// E) dust shape/conn (더스트 모양/연결)
// ============================================================
rule R_DUST_CONN_AXIS_CUT {
forall((l,c,d) in Layer * Cell * Dir) {
// Cand: 더스트가 있고(D=1), 그 방향으로 "연결 후보"가 존재하면 후보 true
// ExistsConnectionCandidate는 주변에 더스트/리피터/토치/블록 등
// 연결될 만한 것이 있는지를 보는 built-in(현재 백엔드 매핑 필요)
def Cand[l,c,d] <-> (D[l,c] and ExistsConnectionCandidate(l,c,d));
}
feature DUST_AUTO_AXIS {
forall((l,c) in Layer * Cell) {
// 축 선택:
// - NS 후보가 하나라도 있으면 AxisNS 가능
// - EW 후보가 하나라도 있으면 AxisEW 가능
// 여기서는 "자동으로" 축을 고르게 만들되,
// 둘 다 동시에 선택은 금지(<=1)로 해서 직선 모양을 강제한다.
def AxisNS[l,c] <-> OR{ Cand[l,c,N], Cand[l,c,S] };
def AxisEW[l,c] <-> OR{ Cand[l,c,E], Cand[l,c,W] };
require AxisNS[l,c] + AxisEW[l,c] <= 1;
// Conn은 선택된 축에 따라 2방향만 true가 된다.
// (AxisNS면 N/S만 연결, AxisEW면 E/W만 연결)
def Conn[l,c,N] <-> AxisNS[l,c];
def Conn[l,c,S] <-> AxisNS[l,c];
def Conn[l,c,E] <-> AxisEW[l,c];
def Conn[l,c,W] <-> AxisEW[l,c];
}
}
feature DUST_ALLOW_CROSS {
forall((l,c) in Layer * Cell) {
// 교차(크로스) 모양을 허용하는 확장점:
// AllowCrossChoice가 true면 Cross를 선택 가능하게 하고
// Conn을 4방향으로 열어주는 식으로 확장할 수 있다.
// (현재는 Cross 선언만 있고 Conn 반영은 아직 스켈레톤)
def Cross[l,c] <-> AllowCrossChoice(l,c);
}
}
}
// ============================================================
// F) dust on (더스트 상태)
// ============================================================
rule R_DUST_ON {
forall(s in Sc) {
forall((l,c) in Layer * Cell) {
// 더스트가 ON이라는 것은:
// - 더스트가 설치되어 있고(D=1)
// - DustSrc(원인 집합) 중 하나라도 존재하면(OR{...})
// => 켜진다
def DP[s,l,c] <-> (D[l,c] and OR{ DustSrc[s,l,c] });
}
}
}
// ============================================================
// G) dust -> block (더스트가 블록을 파워, Conn cutoff 반영)
// ============================================================
rule R_DUST_POWERS_BLOCK {
forall(s in Sc) {
forall((l,c,d) in Layer * Cell * Dir) {
let b = neigh(c,d); // c에서 d방향 이웃 블록 셀
// 더스트가 블록을 파워하는 조건:
// - 더스트가 켜져 있고(DP)
// - 해당 방향으로 연결이 열려 있고(Conn)
// - 이웃 칸에 솔리드 블록이 실제로 존재해야(S[b])
// => 그 블록은 파워 원인으로 "더스트"를 하나 가진다
def DustPowersBlock[s,l,c,d] <-> (DP[s,l,c] and Conn[l,c,d] and S[b]);
// 그 결과를 BlockSrc에 원인으로 추가(“이 블록은 저 더스트 때문에 파워됨”)
add BlockSrc[s,b] += DustPowersBlock(s,l,c,d);
}
}
}
// ============================================================
// H) block power (블록 파워)
// ============================================================
rule R_BLOCK_POWER {
feature MINIMAL_NOT_STRONG {
forall(s in Sc) {
forall(c in Cell) {
// (단순화/근사) 블록 파워를 "TOP 더스트가 켜져 있느냐"로만 정의
// 즉, BP는 더스트 전파 규칙(토치/리피터/간접파워 등)을
// 강하게 모델링하지 않고 최소한으로 쓰겠다는 설정.
// 실제로는: BP[s,c] <-> OR{ BlockSrc[s,c] } 같은 형태가 더 일반적.
def BP[s,c] <-> DP[s,TOP,c];
}
}
}
// else: 블록은 OR(BlockSrc)로 정의하는 정식 버전을 넣을 수 있음
}
// ============================================================
// J) truth table (NOT 게이트 진리표 강제)
// ============================================================
rule R_TRUTH_TABLE_NOT {
// Observe(PIN, s=k)는 "시나리오 k에서 그 핀의 관측값"을 의미.
// 여기서는 시나리오 0에서는 IN=0, 시나리오 1에서는 IN=1로 강제하고,
// OUT은 그 반대로 나오도록 강제해서 NOT 게이트를 합성하게 만든다.
force Observe(IN, s=0) == 0;
force Observe(IN, s=1) == 1;
force Observe(OUT, s=0) == 1;
force Observe(OUT, s=1) == 0;
}
// ============================================================
// K) objective (목적함수)
// ============================================================
objective minimize {
// 가중치 기반 비용 최소화:
// - 더스트 개수 * wD
// - 토치 개수 * wT
// - 솔리드 블록 개수 * wS
// - 리피터 개수 * wR
// 이렇게 하면 “가능하면 적은 부품으로” NOT을 만들도록 유도된다.
wD * sum((l,c) in Layer * Cell) D[l,c]
+ wT * (sum(c in Cell) T_stand[c] + sum((c,d) in Cell * Dir) T_wall[c,d])
+ wS * sum(c in Cell) S[c]
+ wR * sum((c,d) in Cell * Dir) R[c,d]
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels