//// Suduku String author="Professor BAM", ver="1907e", title="SuDuKo", subtitle="Click a cell! *", news="* Click a cell!"; // * noLoop until event. // * hilite row, col, box // * OpenP fix: :s/left/xleft/ // * input from a file (to fill the grid[][] ) // * click, arrows, digit (unless defined). // * check row, col, box (circle the error in red) // * bad & may (red & green) bits // Given => FORCE bad in row, col, box 'p'ut // Try => CLEAR may in row, col, box 'm'1aybe (== "notes") // * +++ setCell(d,r,c) updates all notes row/col/box // * +++ if grid[r][c] != 0 then clear & update all notes. // * ',' does '.' for ALL // TODO: // +++ move lines into grid. // +++ randomize into lines?? String hint0="Click a cell!", hint1="Enter a digit; . to check cell; , to check all", hint2="? for help; q to quit; s to start; i for file input; r to randomize", helpMessage="?"; String filename; String[] lines = { // "World's most difficult" "..53.....", "8......2.", ".7..1.5..", "4....53..", ".1..7...6", "..32...8.", ".6.5....9", "..4....3.", ".....97.." }; int numlines=lines.length; boolean waiting=false; // Waiting for digit. boolean hint=false; // Show rows & columns. boolean debug=false, checking=false; String _=",", COMMA=", ", TAB=" ", SPACE=" ", EQUAL=" = ", SLASH=" / ", L="[", R="]"; Grid g; int difficulty=50; int ni=3, nj=3, nr=3, nc=3; int ww=800, hh=720, xleft=60, ytop=50; int w=64, h=64, wide=w*nc*nj, high=h*nr*ni; int xright=xleft+wide, center=(xleft+xright)/2, ybottom=ytop+high; int xbutton=300, ybutton=0; void setup() { size(800, 720); helpMessage = setHelp(); restart(); } void draw() { background(240, 220, 200); if (keyPressed && key=='?') help(); else g.show(); messages(); noLoop(); // Wait for event. } void help() { fill(#0000FF); text( helpMessage, xleft+20, ytop+20 ); } void messages() { String s=""; fill(#0000FF); textSize(30); text( title, 10, 26 ); textSize(20); text( subtitle, width/3, 20 ); if (checking) text( "checking", width-80, 20 ); if (news.charAt(0) < '0') fill(#FF0000); // RED for errors. text( news, xleft, 44 ); // fill(#0000FF); textSize(16); text( ""+key+"", width-20, 20 ); if (waiting) text( hint1, xleft, ybottom + 20) ; else text( hint0, xleft, ybottom + 20) ; text( hint2, xleft, ybottom + 35) ; if (filename != null) text( "FILE: "+filename+" ("+numlines+")", xleft, ybottom+50 ); s = debug ? " \\ debug" : " \\"; text( s, xright+5, ytop ); // Bottom stuff // text( author, 20, height-30 ); text( ver, 40, height-15 ); textSize(12); text( g.count+SLASH+g.many +" "+difficulty+"%", width-150, height-30 ); float fraction= 100 - 100*( (1.0*g.count) / (1.0*g.many) ); text( fraction, width-140, height-15 ); text( frameCount, width-50, height-2 ); stroke(#FF0000); for (int x=100; xxleft+wide || mouseYytop+high) { news= "*** NOT on a valid cell. ("+ mouseX +COMMA+ mouseY +")"; return; } // Calculate cell placement (on 9x9 grid). int rr=0, cc=0; rr = int( 9.0 * (mouseY-ytop) / high ); cc = int( 9.0 * (mouseX-xleft) / wide ); //-- println( rr, cc, mouseY, mouseX ); if (rr<0 || rr>8 || cc<0 || cc>8) { news= "*** Click was not on a valid cell. ["+ rr +COMMA+ cc +"]"; return; } if (rr==g.rclick && cc==g.cclick) { g.check(); return; } g.click( rr, cc ); // Set clicked cell in grid. news= "Clicked on "+ g.rc() +TAB+ g.g() +TAB+TAB+ Integer.toHexString( g.m() ) +_+ Integer.toHexString( g.b() ) ; waiting=true; } boolean button( float x, float y ) { bug( "button( "+x+_+y+" "+mouseX+_+mouseY ); if (mouseXx+50) return false; if (mouseYy+30) return false; bug( "BUTTON" ); return true; } String setHelp() { String s="", NL="\n"; s += NL + "q quit"; s += NL + "s start from lines."; s += NL + "i input lines from a file."; s += NL + "c copy grid values into lines."; s += NL + "d debugging"; s += NL + "r randomize"; s += NL + "- + increase/decrease difficulty"; s += NL + "[ ] increase/decrease difficulty by 5"; s += NL + ""; s += NL + "CLICK to select a cell, then:"; s += NL + " Enter a number choice"; s += NL + " SHIFT enters a number as given."; s += NL + " DELETE removes a choice"; s += NL + "CLICK again to check row, col, & box for errors."; s += NL + "'.' checks row, col, & box for errors. "; s += NL + "',' or ':' checks entire grid"; s += NL + "press space key to unclick."; s += NL + "p put a preset number"; // ???? s += NL + "o omit a preset number"; s += NL + "y MAY: digit is allowed for this cell."; s += NL + "n BAD: digit is NOT allowed for this cell."; s += NL + ""; return s; } void keyReleased() { loop(); } void keyPressed() { loop(); bug( "KEY: "+str(key) ); if (key=='q') exit(); if (key=='r') randomize(); if (key=='s') g.start( lines ); if (key=='-') randomize( difficulty-1 ); if (key=='+') randomize( difficulty+1 ); if (key=='=') randomize( difficulty+1 ); if (key=='_') randomize( 50 ); if (key=='[') randomize( difficulty-5 ); if (key==']') randomize( difficulty+5 ); if (key=='\\') debug = ! debug; if (key=='i') input(); // +++ copy grid into lines. 'm' ??? // +++ write to file?? // if (key == '.') { g.check(); } if (key == ':') { g.checkAll(); } if (key == ',') { g.updateAll(); } // if (not(waiting)) { // Allow shifted digits (if not waiting). if (key == '!') checking = ! checking; if (key == '@') { } if (key == '#') { } if (key == '$') { } if (key == '%') { } if (key == '^') { } if (key == '&') { } if (key == '*') { } if (key == '(') { } if (key == ')') { } } else { //// WAITING FOR digit INPUT. //// if (key==DELETE) key='0'; if (key>='0' && key<='9') g.setCell( key - '0' ); else if (key >= 'A' && key <='Z' ) g.setCell( key - 'A' +10 ); else if (key < 'A') { if (key == '!') g.setCell( 11 ); if (key == '@') g.setCell( 12 ); if (key == '#') g.setCell( 13 ); if (key == '$') g.setCell( 14 ); if (key == '%') g.setCell( 15 ); if (key == '^') g.setCell( 16 ); if (key == '&') g.setCell( 17 ); if (key == '*') g.setCell( 18 ); if (key == '(') g.setCell( 19 ); if (key == ')') g.setCell( 10 ); } else if (key == ' ') g.unclick(); else waiting=false; } if (key=='p') { // Put a given in this cell! ??? } if (key == CODED) { bug( "KEYCODE: "+keyCode ); if (keyCode == UP) g.up(); else if (keyCode == DOWN) g.down(); else if (keyCode == LEFT) g.left(); else if (keyCode == RIGHT) g.right(); else return; int rr=g.rclick, cc=g.cclick; g.click( rr, cc ); news= "Moved to "+ g.rc() +TAB+ g.g() +TAB+TAB+ Integer.toHexString( g.m() ) +_+ Integer.toHexString( g.b() ) ; waiting=true; } } boolean not(boolean p) { return ! p; } // Read input file // void input() { selectInput("Select a file to process:", "fileSelected"); } void fileSelected(File selection) { println( "fileSelected" ); if (selection == null) { println("Window was closed or the user hit cancel."); } else { filename= selection.getAbsolutePath(); lines = loadStrings( filename ); } print( filename ); numlines=lines.length; println( numlines ); } void restart() { g = new Grid( lines ); } void randomize() { constrain( difficulty, 10, 90 ); g.difficulty= difficulty; //-- g = new Grid(difficulty); g.randomize(difficulty); printDigits( g ); println( "RESET "+g.count+"/"+g.many+_+difficulty+"%" ); } void randomize( int foo ) { difficulty=foo; randomize(); } class Grid { int[][] grid; int ni=3, nj=3, nrows=ni*nj, ncols=ni*nj, count=0, many=0; int i, j, r, c; int rclick=-1, cclick=-1; // Coordinates of clicked-on cell (if any). int tsize=24; int nd = 9; int[] digits = new int[nd+2]; //= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'X' }; // nd=digits.length -2; int[][] may; // bit indicates maybe (allowed) # int[][] bad; // bit indicates bad (due to given!) # //-- int[][] given; // bit indicates given # in row/col/boc //-- int[][] tried; // bit indicates tried # in row/col/boc int difficulty=50, numlines=9; // Grid( String[] lines ) { numlines=lines.length; constrain( numlines, 0, nrows); makeGrid(); start( lines ); } Grid( int difficulty ) { constrain( difficulty, 10, 90 ); this.difficulty=difficulty; makeGrid(); randomize( difficulty ); } void makeGrid() { many = ni *nj * nrows * ncols; grid= new int[nrows][ncols]; may= new int[nrows][ncols]; bad= new int[nrows][ncols]; setDigits(nd); } void start( String[] lines ) { for (int r=0; r0 && d<=9) setCell( -d, r, c ); else setCell( 0, r, c ); setNotes( d, r, c ); // may[r][c]= 0xFFFFFFFF; // bits for 123456789 // bad[r][c]= 0x00000000; } } } void randomize( int difficulty ) { int r, c; int pick= 0; count=0; for (r=0; rdifficulty) { boolean ok=false; while (!ok) { pick= digits[ iran(nd) ]; bug( chance+" > "+difficulty ); bug( " g["+r+_+c+"] = "+pick ); grid[r][c] = -pick; ok = check(r, c); } ++count; bug( "count: "+count+COMMA+chance+COMMA+difficulty ); } else { bug( chance+"<"+difficulty ); } } } say( "New grid: Count="+count+_+many ); unclick(); } void setDigits( int n ) { for (int i=1; i9) digits[i] = 'A' -1 + i; if (i>36) digits[i] = 'a' -1 + i; } } void unclick() { rclick=cclick= -1; } boolean click( int rr, int cc ) { bug( "click("+rr+COMMA+cc+")" ); if (rr<0 || rr>nr*nj || cc<0 || cc>nc*nj) { return err( "Bad click: "+rclick+_+cclick ); } cclick= cc; rclick= rr; if (hint) check(rr, cc); return true; } void show() { int x=xleft, y=ytop; for (r=0; r9) t = t/2; //// Display )nonzero) digit -- black if given; blue if trial. if (dd <0 ) { // pre-defined cells f=givencolor; // BLACK t=t+6; // BOLD y=y+3; } else if (dd >0 ) { if ((badrc&mask) != 0) { // NOT allowed news= "RED"+g.rc(r, c)+badrc+_+mask ; redCircle( x, y ); } if ((mayrc&mask) == 0) { // NOT in the maybe blueCircle( x, y ); say( "BLUE"+g.rc(r, c)+badrc+_+mask ); } } if (dd != 0) { fill(f); textSize(t); text( abs(dd), x+18, y+15+tsize ); } showNotes( d, badrc, mayrc, xx, yy ); return true; }// Grid.showCell() // void showNotes( int d, int badrc, int mayrc, int xx, int yy ) { bug( "showNotes("+d+_+badrc+_+mayrc+_+xx+_+yy ); int x=xx, y=yy; d=abs(d); int mask=1; color badcolor=#FF9999, maycolor=#009900, dotcolor=#66FFFF; // may & bad. int dx= 5+ w/4, dy=5+h/4; // spacing for notes textSize(10); int n=1; for (int j=0; j0 && rclick =0 && rclick 0 && cclick =0 && cclick 0) { // NOTED: force may-bit in this row, col, box } */ if (checking) check( r, c ); } boolean setCell( int dd ) { // ++++ setCell( dd, rclick, cclick ); ??? r=rclick; c=cclick; if (!ok(r, c)) return nosuch( r, c ); int n=grid[rclick][cclick]; //-- println( "setCell "+_+dd ); if (n < 0) { String s= "*** ERROR" + ": Cell "+L+ rclick +COMMA+ cclick +R + " is defined: "+ -n; background(0, 0, 0); rclick=cclick=-1; // Defined cell; flash black. return err(s); } // +++ ??? call setCell(d,r,c) instead ??? grid[rclick][cclick] = dd; news= "Cell ["+ rclick +COMMA+ cclick +"] = "+ dd; return true; } String NOSUCH="*** NO SUCH CELL: "; boolean ok( int r, int c ) { return (r>=0 && c>=0 && r0) mayClear( d, r, c ); } //// CHECK FOR ERRORS //// boolean check() { return check(rclick, cclick); } boolean check( int r, int c ) { // Return false if d is duplicated in the same row, column,. or box. // Also update bad[][] and may[][] int errors=0; if (r<0 || c<0) { say( "*** No such cell: check("+r+_+c+")" ); return false; } int dd= grid[r][c]; int d= abs( dd ); bug( "check("+r+COMMA+c+") dd:"+dd ); if (d == 0) return true; // ROW CHECK // bug( "Check row "+r+" for "+d ); for (int i=0; i0) news= "*** "+ errors+" ERRORS for "+d+" in "+g.rc(r, c); return errors==0; } boolean checkCell( int rr, int cc, int r, int c ) { if (!ok(rr, cc)) return err( "*** "+rr+_+cc ); if (rr==r && cc==c) return true; // Skip same. int nn=grid[rr][cc], dd=grid[r][c]; int n=abs(nn), d=abs(dd); if (dd<0) badSet( d, rr, cc ); if (dd>0) mayClear( d, rr, cc ); //// Check next cell: error if it contains d; set bad or may bits. return (n != d); } boolean checkAll() { int errors=0; rclick=cclick=-1; for (int r=0; r0) clear may bits } } if (errors==0) say( "NO ERRORS!" ); else err( "*** "+errors+" ERRORS FOUND!" ); return; } void badSet( int dd, int r, int c ) { bug( "badSet( "+dd+_+r+_+c+")" ); int d=abs(dd); int b= bad[r][c] |= bitmask(d); // Set bad for this digit. bug( " bad["+r+"]["+c+"] |= "+bitmask(d)+": "+b+_+(~b) ); } void mayClear( int dd, int r, int c ) { println( "mayClear( "+dd+_+r+_+c ); int d=abs(dd); //-- int b= may[r][c] &= ~( 1 << (d-1) ); // Clear may bit int b= may[r][c] &= ~( bitmask(d) ); // Clear may bit println( "may["+r+"]["+c+"] &= ~( 1 << (d-1): "+b+_+(~b) ); } void maySet( int d, int r, int c ) { // ????? not called (yet) >>> //-- may[r][c] |= ~( 1 << (d-1) ); // Set may bit may[r][c] |= ~( bitmask(d) ); // Set may bit } int bitmask( int d ) { return 1 << (abs(d)-1); } // int g() { return grid[rclick][cclick]; } int m() { return may[rclick][cclick]; } int b() { return bad[rclick][cclick]; } String rc() { return rc(rclick, cclick); } String rc( int r, int c ) { return "["+ r +","+ c + "]"; } }// class Grid //