diff --git a/solution/0400-0499/0488.Zuma Game/README.md b/solution/0400-0499/0488.Zuma Game/README.md index 12cc0fa22ce22..2e48221e8d164 100644 --- a/solution/0400-0499/0488.Zuma Game/README.md +++ b/solution/0400-0499/0488.Zuma Game/README.md @@ -118,6 +118,172 @@ class Solution: return -1 ``` +```java +class Solution { + public int findMinStep(String board, String hand) { + final Zuma zuma = Zuma.create(board, hand); + final HashSet visited = new HashSet<>(); + final ArrayList init = new ArrayList<>(); + + visited.add(zuma.board()); + init.add(zuma); + return bfs(init, 0, visited); + } + + private int bfs(ArrayList curr, int k, HashSet visited) { + if (curr.isEmpty()) { + return -1; + } + + final ArrayList next = new ArrayList<>(); + + for (Zuma zuma : curr) { + ArrayList neib = zuma.getNextLevel(k, visited); + if (neib == null) { + return k + 1; + } + + next.addAll(neib); + } + return bfs(next, k + 1, visited); + } +} + +record Zuma(long board, long hand) { + public static Zuma create(String boardStr, String handStr) { + return new Zuma(Zuma.encode(boardStr, false), Zuma.encode(handStr, true)); + } + + public ArrayList getNextLevel(int depth, HashSet visited) { + final ArrayList next = new ArrayList<>(); + final ArrayList handList = this.buildHandList(); + final long[] boardList = new long[32]; + final int size = this.buildBoardList(boardList); + + for (long[] pair : handList) { + for (int i = 0; i < size; ++i) { + final long rawBoard = pruningCheck(boardList[i], pair[0], i * 3, depth); + if (rawBoard == -1) { + continue; + } + + final long nextBoard = updateBoard(rawBoard); + if (nextBoard == 0) { + return null; + } + + if (pair[1] == 0 || visited.contains(nextBoard)) { + continue; + } + + visited.add(nextBoard); + next.add(new Zuma(nextBoard, pair[1])); + } + } + return next; + } + + private long pruningCheck(long insBoard, long ball, int pos, int depth) { + final long L = (insBoard >> (pos + 3)) & 0x7; + final long R = (insBoard >> (pos - 3)) & 0x7; + + if (depth == 0 && (ball != R) && (L != R) || depth > 0 && (ball != R)) { + return -1; + } + return insBoard | (ball << pos); + } + + private long updateBoard(long board) { + long stack = 0; + + for (int i = 0; i < 64; i += 3) { + final long curr = (board >> i) & 0x7; + final long top = (stack) &0x7; + + // pop (if possible) + if ((top > 0) && (curr != top) && (stack & 0x3F) == ((stack >> 3) & 0x3F)) { + stack >>= 9; + if ((stack & 0x7) == top) stack >>= 3; + } + + if (curr == 0) { + // done + break; + } + // push and continue + stack = (stack << 3) | curr; + } + return stack; + } + + private ArrayList buildHandList() { + final ArrayList handList = new ArrayList<>(); + long prevBall = 0; + long ballMask = 0; + + for (int i = 0; i < 16; i += 3) { + final long currBall = (this.hand >> i) & 0x7; + if (currBall == 0) { + break; + } + + if (currBall != prevBall) { + prevBall = currBall; + handList.add( + new long[] {currBall, ((this.hand >> 3) & ~ballMask) | (this.hand & ballMask)}); + } + ballMask = (ballMask << 3) | 0x7; + } + return handList; + } + + private int buildBoardList(long[] buffer) { + int ptr = 0; + long ballMask = 0x7; + long insBoard = this.board << 3; + buffer[ptr++] = insBoard; + + while (true) { + final long currBall = this.board & ballMask; + if (currBall == 0) { + break; + } + + ballMask <<= 3; + insBoard = (insBoard | currBall) & ~ballMask; + buffer[ptr++] = insBoard; + } + return ptr; + } + + private static long encode(String stateStr, boolean sortFlag) { + final char[] stateChars = stateStr.toCharArray(); + if (sortFlag) { + Arrays.sort(stateChars); + } + + long stateBits = 0; + for (char ch : stateChars) { + stateBits = (stateBits << 3) | Zuma.encode(ch); + } + return stateBits; + } + + private static long encode(char ch) { + return switch (ch) { + case 'R' -> 0x1; + case 'G' -> 0x2; + case 'B' -> 0x3; + case 'W' -> 0x4; + case 'Y' -> 0x5; + case ' ' -> 0x0; + default -> + throw new IllegalArgumentException("Invalid char: " + ch); + }; + } +} +``` + diff --git a/solution/0400-0499/0488.Zuma Game/README_EN.md b/solution/0400-0499/0488.Zuma Game/README_EN.md index 430e943c21a41..59c8605c4b501 100644 --- a/solution/0400-0499/0488.Zuma Game/README_EN.md +++ b/solution/0400-0499/0488.Zuma Game/README_EN.md @@ -102,6 +102,172 @@ class Solution: return -1 ``` +```java +class Solution { + public int findMinStep(String board, String hand) { + final Zuma zuma = Zuma.create(board, hand); + final HashSet visited = new HashSet<>(); + final ArrayList init = new ArrayList<>(); + + visited.add(zuma.board()); + init.add(zuma); + return bfs(init, 0, visited); + } + + private int bfs(ArrayList curr, int k, HashSet visited) { + if (curr.isEmpty()) { + return -1; + } + + final ArrayList next = new ArrayList<>(); + + for (Zuma zuma : curr) { + ArrayList neib = zuma.getNextLevel(k, visited); + if (neib == null) { + return k + 1; + } + + next.addAll(neib); + } + return bfs(next, k + 1, visited); + } +} + +record Zuma(long board, long hand) { + public static Zuma create(String boardStr, String handStr) { + return new Zuma(Zuma.encode(boardStr, false), Zuma.encode(handStr, true)); + } + + public ArrayList getNextLevel(int depth, HashSet visited) { + final ArrayList next = new ArrayList<>(); + final ArrayList handList = this.buildHandList(); + final long[] boardList = new long[32]; + final int size = this.buildBoardList(boardList); + + for (long[] pair : handList) { + for (int i = 0; i < size; ++i) { + final long rawBoard = pruningCheck(boardList[i], pair[0], i * 3, depth); + if (rawBoard == -1) { + continue; + } + + final long nextBoard = updateBoard(rawBoard); + if (nextBoard == 0) { + return null; + } + + if (pair[1] == 0 || visited.contains(nextBoard)) { + continue; + } + + visited.add(nextBoard); + next.add(new Zuma(nextBoard, pair[1])); + } + } + return next; + } + + private long pruningCheck(long insBoard, long ball, int pos, int depth) { + final long L = (insBoard >> (pos + 3)) & 0x7; + final long R = (insBoard >> (pos - 3)) & 0x7; + + if (depth == 0 && (ball != R) && (L != R) || depth > 0 && (ball != R)) { + return -1; + } + return insBoard | (ball << pos); + } + + private long updateBoard(long board) { + long stack = 0; + + for (int i = 0; i < 64; i += 3) { + final long curr = (board >> i) & 0x7; + final long top = (stack) &0x7; + + // pop (if possible) + if ((top > 0) && (curr != top) && (stack & 0x3F) == ((stack >> 3) & 0x3F)) { + stack >>= 9; + if ((stack & 0x7) == top) stack >>= 3; + } + + if (curr == 0) { + // done + break; + } + // push and continue + stack = (stack << 3) | curr; + } + return stack; + } + + private ArrayList buildHandList() { + final ArrayList handList = new ArrayList<>(); + long prevBall = 0; + long ballMask = 0; + + for (int i = 0; i < 16; i += 3) { + final long currBall = (this.hand >> i) & 0x7; + if (currBall == 0) { + break; + } + + if (currBall != prevBall) { + prevBall = currBall; + handList.add( + new long[] {currBall, ((this.hand >> 3) & ~ballMask) | (this.hand & ballMask)}); + } + ballMask = (ballMask << 3) | 0x7; + } + return handList; + } + + private int buildBoardList(long[] buffer) { + int ptr = 0; + long ballMask = 0x7; + long insBoard = this.board << 3; + buffer[ptr++] = insBoard; + + while (true) { + final long currBall = this.board & ballMask; + if (currBall == 0) { + break; + } + + ballMask <<= 3; + insBoard = (insBoard | currBall) & ~ballMask; + buffer[ptr++] = insBoard; + } + return ptr; + } + + private static long encode(String stateStr, boolean sortFlag) { + final char[] stateChars = stateStr.toCharArray(); + if (sortFlag) { + Arrays.sort(stateChars); + } + + long stateBits = 0; + for (char ch : stateChars) { + stateBits = (stateBits << 3) | Zuma.encode(ch); + } + return stateBits; + } + + private static long encode(char ch) { + return switch (ch) { + case 'R' -> 0x1; + case 'G' -> 0x2; + case 'B' -> 0x3; + case 'W' -> 0x4; + case 'Y' -> 0x5; + case ' ' -> 0x0; + default -> + throw new IllegalArgumentException("Invalid char: " + ch); + }; + } +} +``` + diff --git a/solution/0400-0499/0488.Zuma Game/Solution.java b/solution/0400-0499/0488.Zuma Game/Solution.java new file mode 100644 index 0000000000000..3233e4eda9d1c --- /dev/null +++ b/solution/0400-0499/0488.Zuma Game/Solution.java @@ -0,0 +1,163 @@ +class Solution { + public int findMinStep(String board, String hand) { + final Zuma zuma = Zuma.create(board, hand); + final HashSet visited = new HashSet<>(); + final ArrayList init = new ArrayList<>(); + + visited.add(zuma.board()); + init.add(zuma); + return bfs(init, 0, visited); + } + + private int bfs(ArrayList curr, int k, HashSet visited) { + if (curr.isEmpty()) { + return -1; + } + + final ArrayList next = new ArrayList<>(); + + for (Zuma zuma : curr) { + ArrayList neib = zuma.getNextLevel(k, visited); + if (neib == null) { + return k + 1; + } + + next.addAll(neib); + } + return bfs(next, k + 1, visited); + } +} + +record Zuma(long board, long hand) { + public static Zuma create(String boardStr, String handStr) { + return new Zuma(Zuma.encode(boardStr, false), Zuma.encode(handStr, true)); + } + + public ArrayList getNextLevel(int depth, HashSet visited) { + final ArrayList next = new ArrayList<>(); + final ArrayList handList = this.buildHandList(); + final long[] boardList = new long[32]; + final int size = this.buildBoardList(boardList); + + for (long[] pair : handList) { + for (int i = 0; i < size; ++i) { + final long rawBoard = pruningCheck(boardList[i], pair[0], i * 3, depth); + if (rawBoard == -1) { + continue; + } + + final long nextBoard = updateBoard(rawBoard); + if (nextBoard == 0) { + return null; + } + + if (pair[1] == 0 || visited.contains(nextBoard)) { + continue; + } + + visited.add(nextBoard); + next.add(new Zuma(nextBoard, pair[1])); + } + } + return next; + } + + private long pruningCheck(long insBoard, long ball, int pos, int depth) { + final long L = (insBoard >> (pos + 3)) & 0x7; + final long R = (insBoard >> (pos - 3)) & 0x7; + + if (depth == 0 && (ball != R) && (L != R) || depth > 0 && (ball != R)) { + return -1; + } + return insBoard | (ball << pos); + } + + private long updateBoard(long board) { + long stack = 0; + + for (int i = 0; i < 64; i += 3) { + final long curr = (board >> i) & 0x7; + final long top = (stack) &0x7; + + // pop (if possible) + if ((top > 0) && (curr != top) && (stack & 0x3F) == ((stack >> 3) & 0x3F)) { + stack >>= 9; + if ((stack & 0x7) == top) stack >>= 3; + } + + if (curr == 0) { + // done + break; + } + // push and continue + stack = (stack << 3) | curr; + } + return stack; + } + + private ArrayList buildHandList() { + final ArrayList handList = new ArrayList<>(); + long prevBall = 0; + long ballMask = 0; + + for (int i = 0; i < 16; i += 3) { + final long currBall = (this.hand >> i) & 0x7; + if (currBall == 0) { + break; + } + + if (currBall != prevBall) { + prevBall = currBall; + handList.add( + new long[] {currBall, ((this.hand >> 3) & ~ballMask) | (this.hand & ballMask)}); + } + ballMask = (ballMask << 3) | 0x7; + } + return handList; + } + + private int buildBoardList(long[] buffer) { + int ptr = 0; + long ballMask = 0x7; + long insBoard = this.board << 3; + buffer[ptr++] = insBoard; + + while (true) { + final long currBall = this.board & ballMask; + if (currBall == 0) { + break; + } + + ballMask <<= 3; + insBoard = (insBoard | currBall) & ~ballMask; + buffer[ptr++] = insBoard; + } + return ptr; + } + + private static long encode(String stateStr, boolean sortFlag) { + final char[] stateChars = stateStr.toCharArray(); + if (sortFlag) { + Arrays.sort(stateChars); + } + + long stateBits = 0; + for (char ch : stateChars) { + stateBits = (stateBits << 3) | Zuma.encode(ch); + } + return stateBits; + } + + private static long encode(char ch) { + return switch (ch) { + case 'R' -> 0x1; + case 'G' -> 0x2; + case 'B' -> 0x3; + case 'W' -> 0x4; + case 'Y' -> 0x5; + case ' ' -> 0x0; + default -> + throw new IllegalArgumentException("Invalid char: " + ch); + }; + } +}