3 Replies - 1378 Views - Last Post: 30 August 2011 - 12:31 PM Rate Topic: -----

#1 NotarySojac  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 53
  • View blog
  • Posts: 428
  • Joined: 30-September 10

Sudoku on Rails: Code critiques plz

Posted 29 August 2011 - 03:41 PM

Hey, Remember a while back when we did that Sudoku challenge? Well here's the efficiency winner's project snapped into a rails project I whipped together today. Credits to Tethik for doing basically 100% of the logic in this application (see the contents of the CODE directory to see his work). I made a minor (less artful) alteration to allow it to be fed a puzzle through the command line directly.

Here's the list of technologies used:

1) Rails
2) jQuery/ javascript
3) HTML
4) CSS
5) Ruby


Wow, that's a lot of BS a programmer needs to know in order to pull off a rails app. You'll probably note that my HTML/CSS is pretty rough, but that's the way it's gotta be until I get more research points (I'm so disinterested with HTML that it's hard for me to study, but javascript helps).


For those of you who don't know rails, you might benafit from this passage.
Spoiler


So all of the "rails programming" went into the controller. I used these commands to get started I think:
$  rails new sudoku
$  cd sudoku
$  mkdir CODE
$  rails g scaffold solver



From there, I edited the solvers_controller.rb file and that was the extent of Rails knowledge I needed (I know so many, many, many hours more that which I didn't need, and yet, I need so many hours more before I'm a confident rails dev).

--------------------- SETUP -----------------------------------------------------

The complete source code can be downloaded from github by any usual means.
https://github.com/T.../zipball/master
Once you open to the directory with "app" in it, copy all those files and place them on your server (or do "git clone").

Once you have it in place on your rails server, you need to make sure CODE/Sudoku.rb can be executed by rails ($ chmod 0744 CODE/Sudoku.rb did the trick for me on debian.)

Other than that, there are no tricks to it. You can go ahead and do
$  rails server



And you will be able to see Tethik's artful work in HTML format (so sorry about not glossing it up everybody...)
http://localhost:3000/solver.html





--------------------------------------------------------------------------------------------


::Codes of interest::


Rails

Here's where the rails server recieves the submission from the web browser when he clicks "SOLVE" or w/e I wrote.

  # POST /solvers
  # POST /solvers.xml
  def create
    #@solver = Solver.new(params[:solver])
    
    @solver = params[:solver][:param]
    
    if puzzle_valid?
      respz = %x[cd CODE;./Sudoku.rb #{@solver}]     # Is there a way to set a timeout limit so this process get's shutdown after, say 2 seconds, and if it wasn't finished, we do some error code?
      
      time_start = respz.index(":") + 2
      new_line_loc = respz.index("\n")
      time_end = new_line_loc - 21
      
      timez = respz[time_start, time_end]
      
      # check that the puzzle was solved, it might have not been
      if respz.length < 55
        puts "NO SOLUTION FOR THIS PUZZLE!!!"
        @solver = { :time => timez, :grid => "none" }
      else
        grid_start = respz.index(",") - 1
        respz = respz[grid_start, respz.length].gsub("\n", ",").chop
        
        @solver = { :time => timez, :grid => respz }
      end
    end
    
    respond_to do |format|
      #format.html { redirect_to(@solver, :notice => 'Solver was successfully created.') }
      #format.js { render :inline => 'hi' }
      format.js { render :json => @solver }
    end
  end

  def puzzle_valid?
    if @solver =~ /[a-mo-zA-Z_]/          # restrict the string to just commas, numbers 1-9, and "n at the end"
      @solver = { :grid => "none" }
      return false
    end
    
    if @solver =~ /\s|\;|\`/ or @solver.length > 170
      puts "Hax0rz attempt detected"
      @solver = { :grid => "none" }
      return false
    end
    return true
  end



I'm still pretty green when it comes to rails, so plz tell me what you think of this section because I want to get better. Also, is the code safe? Should I do something to prevent DOS attacks? Also, how does this method of processing scale?



jQuery

<script>
jQuery.ajaxSetup({ 
  async: false    // makes it so it waits after sending the post request, aka plain old JAX
})

var glob;
function submitaroony(){
	var submission = "";
	
	for (var a = "a"; a != 'j'; a = incrementLetter(a))
	{
		for (var i = 0; i < 9; i = i + 1)
		{
			var val = $('#' + a + i).attr('value');
			submission += val;
			
			if (i != 8)
				submission += ",";
			else
				submission += 'n';
		}
	}
	
	//submission += ';';
	
	glob = $.parseJSON(
		$.post('solvers.js', 
		{ solver: {param: submission}},
		null,
		"text").responseText);
	
	if (glob.grid == "none")
	{
		$('#time').html("This Puzzle Is Erroneous And Cannot Be Solved");
	}
	else
	{
		populateGrid(glob.grid);
		$('#time').html("Brute Forced in " + glob.time + " seconds!");
	}
	
	
}

function populateGrid(grid){
	grid = grid.replace(/,/g, '');
	var slot = 0;
	
	for (var a = "a"; a != 'j'; a = incrementLetter(a))
	{
		for (var i = 0; i < 9; i = i + 1)
		{
			//alert(grid[slot]);
			$('#' + a + i).attr('value', grid[slot]);
			
			slot = slot + 1;
		}
	}
}


var alphaChars = "abcdefghij";
function incrementLetter(letterToIncrement){
 //find where the letter is at in the alphaChars string
 var indexOfLetter = alphaChars.search(letterToIncrement);
 //if it's not the last letter, then return the next
 //letter in the string
 if (indexOfLetter+1 < alphaChars.length) {
   return(alphaChars.charAt(indexOfLetter+1));
 }
 //otherwise return the input letter
 else{
   return(letterToIncrement);
 }
}
</script>



To me, this code is less interesting, but perhaps someone wanted to see it.




HTML (all of it, including what I showed in jQuery)
<li>I need a big grid with input boxes</li>
<li>I need to get those input boxes named with a good convention</li>

<li>I need those input boxes to all get submitted as an array when the button is clicked</li>

<li>I need the ajax response to solve the puzzle, or I need an alert message that says "NO SOLUTION!  WTF"</li>

<style>
	input{
		width: 16px;
		
	}
	
	td span{
		background: green;
		width:16px;
		position:relative;
	}
	#spacer{
		background: black;
	}
</style>

<form>
	<table>
		<tr>
			<td><input id="a0" type="text" value="5" maxlength=1> </input></td>
			<td><input id="a1" type="text" value="3" maxlength=1> </input></td>
			<td><input id="a2" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			
			<td><input id="a3" type="text" value="" maxlength=1> </input></td>
			<td><input id="a4" type="text" value="7" maxlength=1> </input></td>
			<td><input id="a5" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="a6" type="text" value="" maxlength=1> </input></td>
			<td><input id="a7" type="text" value="" maxlength=1> </input></td>
			<td><input id="a8" type="text" value="" maxlength=1> </input></td>
		</tr>
		
		<tr>
			<td><input id="b0" type="text" value="6" maxlength=1> </input></td>
			<td><input id="b1" type="text" value="" maxlength=1> </input></td>
			<td><input id="b2" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="b3" type="text" value="1" maxlength=1> </input></td>
			<td><input id="b4" type="text" value="9" maxlength=1> </input></td>
			<td><input id="b5" type="text" value="5" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="b6" type="text" value="" maxlength=1> </input></td>
			<td><input id="b7" type="text" value="" maxlength=1> </input></td>
			<td><input id="b8" type="text" value="" maxlength=1> </input></td>
		</tr>
		
		<tr>
			<td><input id="c0" type="text" value="" maxlength=1> </input></td>
			<td><input id="c1" type="text" value="9" maxlength=1> </input></td>
			<td><input id="c2" type="text" value="8" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="c3" type="text" value="" maxlength=1> </input></td>
			<td><input id="c4" type="text" value="" maxlength=1> </input></td>
			<td><input id="c5" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="c6" type="text" value="" maxlength=1> </input></td>
			<td><input id="c7" type="text" value="6" maxlength=1> </input></td>
			<td><input id="c8" type="text" value="" maxlength=1> </input></td>
		</tr>
		
		<tr>
			<td colspan="11"><hr/></td>
		</tr>
		
		<tr>
			<td><input id="d0" type="text" value="8" maxlength=1> </input></td>
			<td><input id="d1" type="text" value="" maxlength=1> </input></td>
			<td><input id="d2" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="d3" type="text" value="" maxlength=1> </input></td>
			<td><input id="d4" type="text" value="6" maxlength=1> </input></td>
			<td><input id="d5" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="d6" type="text" value="" maxlength=1> </input></td>
			<td><input id="d7" type="text" value="" maxlength=1> </input></td>
			<td><input id="d8" type="text" value="3" maxlength=1> </input></td>
		</tr>
		
		<tr>
			<td><input id="e0" type="text" value="4" maxlength=1> </input></td>
			<td><input id="e1" type="text" value="" maxlength=1> </input></td>
			<td><input id="e2" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="e3" type="text" value="8" maxlength=1> </input></td>
			<td><input id="e4" type="text" value="" maxlength=1> </input></td>
			<td><input id="e5" type="text" value="3" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="e6" type="text" value="" maxlength=1> </input></td>
			<td><input id="e7" type="text" value="" maxlength=1> </input></td>
			<td><input id="e8" type="text" value="1" maxlength=1> </input></td>
		</tr>
		
		<tr>
			<td><input id="f0" type="text" value="7" maxlength=1> </input></td>
			<td><input id="f1" type="text" value="" maxlength=1> </input></td>
			<td><input id="f2" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="f3" type="text" value="" maxlength=1> </input></td>
			<td><input id="f4" type="text" value="2" maxlength=1> </input></td>
			<td><input id="f5" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="f6" type="text" value="" maxlength=1> </input></td>
			<td><input id="f7" type="text" value="" maxlength=1> </input></td>
			<td><input id="f8" type="text" value="6" maxlength=1> </input></td>
		</tr>
		
		<tr>
			<td colspan="11"><hr/></td>
		</tr>
		
		<tr>
			<td><input id="g0" type="text" value="" maxlength=1> </input></td>
			<td><input id="g1" type="text" value="6" maxlength=1> </input></td>
			<td><input id="g2" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="g3" type="text" value="" maxlength=1> </input></td>
			<td><input id="g4" type="text" value="" maxlength=1> </input></td>
			<td><input id="g5" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="g6" type="text" value="2" maxlength=1> </input></td>
			<td><input id="g7" type="text" value="8" maxlength=1> </input></td>
			<td><input id="g8" type="text" value="" maxlength=1> </input></td>
		</tr>
		
		<tr>
			<td><input id="h0" type="text" value="" maxlength=1> </input></td>
			<td><input id="h1" type="text" value="" maxlength=1> </input></td>
			<td><input id="h2" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="h3" type="text" value="4" maxlength=1> </input></td>
			<td><input id="h4" type="text" value="1" maxlength=1> </input></td>
			<td><input id="h5" type="text" value="9" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="h6" type="text" value="" maxlength=1> </input></td>
			<td><input id="h7" type="text" value="" maxlength=1> </input></td>
			<td><input id="h8" type="text" value="5" maxlength=1> </input></td>
		</tr>
		
		<tr>
			<td><input id="i0" type="text" value="" maxlength=1> </input></td>
			<td><input id="i1" type="text" value="" maxlength=1> </input></td>
			<td><input id="i2" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="i3" type="text" value="" maxlength=1> </input></td>
			<td><input id="i4" type="text" value="8" maxlength=1> </input></td>
			<td><input id="i5" type="text" value="" maxlength=1> </input></td>
			<td id="spacer"></td>
			
			<td><input id="i6" type="text" value="" maxlength=1> </input></td>
			<td><input id="i7" type="text" value="7" maxlength=1> </input></td>
			<td><input id="i8" type="text" value="9" maxlength=1> </input></td>
		</tr>
		
	</table>
	<input type="button" value="SOLVE!" style="width:190px;" onclick="submitaroony()"></input>
</form>

<script type="text/javascript" src="/javascripts/jquery-1.6.2.min.js"></script>

<span id="time"></span>

<script>
jQuery.ajaxSetup({ 
  async: false    // makes it so it waits after sending the post request, aka plain old JAX
})

var glob;
function submitaroony(){
	var submission = "";
	
	for (var a = "a"; a != 'j'; a = incrementLetter(a))
	{
		for (var i = 0; i < 9; i = i + 1)
		{
			var val = $('#' + a + i).attr('value');
			submission += val;
			
			if (i != 8)
				submission += ",";
			else
				submission += 'n';
		}
	}
	
	//submission += ';';
	
	glob = $.parseJSON(
		$.post('solvers.js', 
		{ solver: {param: submission}},
		null,
		"text").responseText);
	
	if (glob.grid == "none")
	{
		$('#time').html("This Puzzle Is Erroneous And Cannot Be Solved");
	}
	else
	{
		populateGrid(glob.grid);
		$('#time').html("Brute Forced in " + glob.time + " seconds!");
	}
	
	
}

function populateGrid(grid){
	grid = grid.replace(/,/g, '');
	var slot = 0;
	
	for (var a = "a"; a != 'j'; a = incrementLetter(a))
	{
		for (var i = 0; i < 9; i = i + 1)
		{
			//alert(grid[slot]);
			$('#' + a + i).attr('value', grid[slot]);
			
			slot = slot + 1;
		}
	}
}


var alphaChars = "abcdefghij";
function incrementLetter(letterToIncrement){
 //find where the letter is at in the alphaChars string
 var indexOfLetter = alphaChars.search(letterToIncrement);
 //if it's not the last letter, then return the next
 //letter in the string
 if (indexOfLetter+1 < alphaChars.length) {
   return(alphaChars.charAt(indexOfLetter+1));
 }
 //otherwise return the input letter
 else{
   return(letterToIncrement);
 }
}
</script>



I did this mockup style because I didn't want to have a ton of separate web files running around, obfuscating the issue.

Is This A Good Question/Topic? 0
  • +

Replies To: Sudoku on Rails: Code critiques plz

#2 Skaggles  Icon User is offline

  • THE PEN IS MIGHTIER
  • member icon





Reputation: 251
  • View blog
  • Posts: 640
  • Joined: 01-March 09

Re: Sudoku on Rails: Code critiques plz

Posted 29 August 2011 - 08:21 PM

This would be great to post in the Share Your Project thread. I read the whole thing thinking you had a problem with your code. I think you're likely to get much faster criticism there.

This post has been edited by Skaggles: 29 August 2011 - 08:34 PM

Was This Post Helpful? 0
  • +
  • -

#3 Tethik  Icon User is offline

  • D.I.C Head

Reputation: 17
  • View blog
  • Posts: 61
  • Joined: 14-March 10

Re: Sudoku on Rails: Code critiques plz

Posted 30 August 2011 - 09:22 AM

One suggestion from first glance would be to create the inputs dynamically instead of writing them all out manually. That way you avoid code repetition and make it easier to change the dimensions of the board. I'm not sure if my class can handle different dimensions at the moment but I believe it can be changed to cater to up to 32x32 size grids (so many bits in an integer).

If you're worried about DOS attacks I guess you could try limiting the amount of users connected to the page.. Also I'm not sure what the worst case scenario is for the solver, but you may want to try and find it (hint: wikipedia sudoku, I use an algorithm from there) and fix the solver accordingly.

I know nothing about rails in itself though so I can't give you much advice on that.
Was This Post Helpful? 0
  • +
  • -

#4 NotarySojac  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 53
  • View blog
  • Posts: 428
  • Joined: 30-September 10

Re: Sudoku on Rails: Code critiques plz

Posted 30 August 2011 - 12:31 PM

View PostTethik, on 30 August 2011 - 09:22 AM, said:

One suggestion from first glance would be to create the inputs dynamically instead of writing them all out manually. That way you avoid code repetition and make it easier to change the dimensions of the board. I'm not sure if my class can handle different dimensions at the moment but I believe it can be changed to cater to up to 32x32 size grids (so many bits in an integer).

If you're worried about DOS attacks I guess you could try limiting the amount of users connected to the page.. Also I'm not sure what the worst case scenario is for the solver, but you may want to try and find it (hint: wikipedia sudoku, I use an algorithm from there) and fix the solver accordingly.

I know nothing about rails in itself though so I can't give you much advice on that.


Thanks for the suggestion. I just completed the work.

If you'd like to get into web apps, I've written some material on setting up a test machine for rails apps. It's here, but it's based on debian. I recommend setting it up in a VM so if things go wrong you can always clear. I can also answer any questions about it, for anyone who has trouble. The average Rails developer salary is in the $99k dollar range (I don't have a Rails job yet, but I'm getting pretty good so maybe I'll start asking around after a couple more projects).

To prevent DOS, I was thinking there might be a rails trick to forcing any external processes to return after a given time, but it's kinda tricky googling for some of the features of rails.

Quote

This would be great to post in the Share Your Project thread. I read the whole thing thinking you had a problem with your code. I think you're likely to get much faster criticism there.


I ran into that section just yesterday actually, but I was thinking it might be better to get more Ruby and Rails specific advice from in here. Plus I'm hoping we can get more foot traffic in here if there are more discussion posts.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1