001package ball.game.ant.taskdefs; 002/*- 003 * ########################################################################## 004 * Game Applications and Utilities 005 * $Id: SudokuTask.java 5285 2020-02-05 04:23:21Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-game/trunk/src/main/java/ball/game/ant/taskdefs/SudokuTask.java $ 007 * %% 008 * Copyright (C) 2010 - 2020 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import ball.game.sudoku.Cell; 024import ball.game.sudoku.Puzzle; 025import ball.game.sudoku.Rule; 026import ball.game.sudoku.RuleOfElimination; 027import ball.util.ant.taskdefs.AnnotatedAntTask; 028import ball.util.ant.taskdefs.AntTask; 029import ball.util.ant.taskdefs.ClasspathDelegateAntTask; 030import ball.util.ant.taskdefs.ConfigurableAntTask; 031import java.util.ServiceLoader; 032import lombok.Getter; 033import lombok.NoArgsConstructor; 034import lombok.Setter; 035import lombok.ToString; 036import lombok.experimental.Accessors; 037import org.apache.tools.ant.BuildException; 038import org.apache.tools.ant.Task; 039import org.apache.tools.ant.util.ClasspathUtils; 040 041import static org.apache.commons.lang3.StringUtils.EMPTY; 042 043/** 044 * {@link.uri http://ant.apache.org/ Ant} {@link Task} to solve Sudoku. 045 * 046 * {@ant.task} 047 * 048 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 049 * @version $Revision: 5285 $ 050 */ 051@AntTask("sudoku") 052@NoArgsConstructor @ToString 053public class SudokuTask extends Task implements AnnotatedAntTask, 054 ClasspathDelegateAntTask, 055 ConfigurableAntTask { 056 @Getter @Setter @Accessors(chain = true, fluent = true) 057 private ClasspathUtils.Delegate delegate = null; 058 private final Puzzle puzzle = new Puzzle(); 059 060 public Puzzle getPuzzle() { return puzzle; } 061 public void setPuzzle(String string) { parse(string); } 062 063 public void addText(String text) { parse(text); } 064 065 private void parse(String string) { 066 string = string.replaceAll("[\\p{Space}]+", EMPTY); 067 string = string.replaceAll("[^1-9]", "."); 068 069 int i = 0; 070 071 for (Cell cell : puzzle.values()) { 072 if (i < string.length()) { 073 if (string.charAt(i) != '.') { 074 cell.retainAll((int) string.charAt(i) - '0'); 075 } 076 077 i += 1; 078 } else { 079 break; 080 } 081 } 082 } 083 084 @Override 085 public void init() throws BuildException { 086 super.init(); 087 ClasspathDelegateAntTask.super.init(); 088 ConfigurableAntTask.super.init(); 089 } 090 091 @Override 092 public void execute() throws BuildException { 093 super.execute(); 094 AnnotatedAntTask.super.execute(); 095 096 ServiceLoader<Rule> loader = 097 ServiceLoader.load(Rule.class, getClassLoader()); 098 Puzzle puzzle = getPuzzle(); 099 100 try { 101 log(puzzle); 102 103 if (! puzzle.isSolved()) { 104 apply(new RuleOfElimination(), puzzle); 105 } 106 107 while (! puzzle.isSolved()) { 108 boolean modified = false; 109 110 for (Rule rule : loader) { 111 modified |= apply(rule, puzzle); 112 113 if (puzzle.isSolved()) { 114 break; 115 } 116 } 117 118 if (! modified) { 119 throw new BuildException("Unsolved puzzle"); 120 } 121 } 122 } catch (BuildException exception) { 123 throw exception; 124 } catch (Throwable throwable) { 125 throwable.printStackTrace(); 126 throw new BuildException(throwable); 127 } 128 } 129 130 private boolean apply(Rule rule, Puzzle puzzle) throws Exception { 131 if (! puzzle.isLegal()) { 132 throw new BuildException("Illegal puzzle"); 133 } 134 135 boolean modified = rule.applyTo(puzzle); 136 137 if (modified) { 138 log(); 139 log(rule.getClass().getCanonicalName()); 140 log(puzzle); 141 } 142 143 return modified; 144 } 145}