El código que sigue implanta un jugador de tres-en-raya.
[~/sinatra/sinatra-tictactoe/sinatra-tictactoe-ajax(master)]$ tree
.
|--- Gemfile
|--- Gemfile.lock
|--- Procfile
|--- Rakefile
|--- Readme.md
|--- app.rb
|--- public
| |--- css
| | |--- app.css
| | `--- style.css
| |--- images
| | |--- blackboard.jpg
| | |--- circle.gif
| | `--- cross.gif
| `--- js
| `--- app.js
`--- views
|--- final.erb
|--- final.haml
|--- game.erb
|--- game.haml
|--- layout.erb
|--- layout.haml
`--- styles.scss
5 directories, 19 files
[~/sinatra/sinatra-tictactoe/sinatra-tictactoe-ajax(master)]$ cat Rakefile desc "run server" task :default do sh "bundle exec ruby app.rb" end desc "install dependencies" task :install do sh "bundle install" end ### desc 'build css' task :css do sh "sass views/styles.scss public/css/style.css" end
[~/sinatra/sinatra-tictactoe/sinatra-tictactoe-ajax(master)]$ cat views/game.haml
.screen
.gameboard
- HORIZONTALS.each do |row|
.gamerow
- row.each do |p|
%a(href=p)
%div{:id => "#{p}", :class => "cell #{b[p]}"}
.message
%h1= m
[~/sinatra/sinatra-tictactoe/sinatra-tictactoe-ajax(master)]$ cat views/layout.haml
!!!
%html
%head
%title tic tac toe
-#%link{:rel=>"stylesheet", :href=>"/css/app.css", :type=>"text/css"}
-# dynamically accessed
-#%link{:rel=>"stylesheet", :href=>"/styles.css", :type=>"text/css"}
-# statically compiled
%link{:rel=>"stylesheet", :href=>"css/style.css", :type=>"text/css"}
%script{:type=>"text/javascript", :src=>"http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"}
%script{:type=>"text/javascript", :src=>"/js/app.js"}
%body
= yield
styles.scss puede compilarse dinámicamente. Véase el fragmento de código
que empieza por
get '/styles.css' do
en app.rb
<!DOCTYPE html>
<html>
<head>
<title>tic tac toe</title>
<link href='css/style.css' rel='stylesheet' type='text/css'>
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js' type='text/javascript'></script>
<script src='/js/app.js' type='text/javascript'></script>
</head>
<body>
<div class='screen'>
<div class='gameboard'>
<div class='gamerow'>
<a href='a1'>
<div class='cell ' id='a1'></div>
</a>
<a href='a2'>
<div class='cell ' id='a2'></div>
</a>
<a href='a3'>
<div class='cell ' id='a3'></div>
</a>
</div>
<div class='gamerow'>
<a href='b1'>
<div class='cell ' id='b1'></div>
</a>
<a href='b2'>
<div class='cell circle' id='b2'></div>
</a>
<a href='b3'>
<div class='cell ' id='b3'></div>
</a>
</div>
<div class='gamerow'>
<a href='c1'>
<div class='cell ' id='c1'></div>
</a>
<a href='c2'>
<div class='cell ' id='c2'></div>
</a>
<a href='c3'>
<div class='cell cross' id='c3'></div>
</a>
</div>
<div class='message'>
<h1></h1>
</div>
</div>
</div>
</body>
</html>
~/sinatra/sinatra-tictactoe/sinatra-tictactoe-ajax(master)]$ cat views/styles.scss
$red: #903;
$black: #444;
$white: #fff;
$ull: #9900FF;
$pink: #F9A7B0;
$main-font: Helvetica, Arial, sans-serif;
$message-font: 22px/1;
$board-left: 300px;
$board-margin: 0 auto;
$board-size: 500px;
$opacity: 0.8;
$cell-width: $board-size/8.5;
$cell-height: $board-size/8.5;
$cell-margin: $cell-width/12;
$cell-padding: $cell-width/1.3;
$background: "/images/blackboard.jpg";
$cross: "/images/cross.gif";
$circle: "/images/circle.gif";
body {
// background-color: lightgrey;
font-family: $main-font;
background: url($background) repeat; background-size: cover;
}
.gameboard { //margin-left: $board-left;
width: $board-size;
margin: $board-margin;
text-align:center;
}
.gamerow { clear: both; }
.cell { color: blue;
background-color: white;
opacity: $opacity;
width: $cell-width;
height: $cell-height;
margin: $cell-margin;
padding: $cell-padding;
&:hover {
color: black ;
background-color: $ull;
}
float: left;
}
@mixin game-piece($image) {
background: url($image) no-repeat; background-size: cover;
}
.cross { @include game-piece($cross); }
.circle { @include game-piece($circle); }
.base-font { color: $pink; font: $message-font $main-font; }
.message {
@extend .base-font;
display: inline;
background-color:transparent;
}
In order to declare the processes that make our app, and scale them individually, we need to be able to tell Heroku what these processes are.
The Procfile is a simple YAML file which sits in the root of your application code and is pushed to your application when you deploy. This file contains a definition of every process you require in your application, and how that process should be started.
[~/sinatra/sinatra-tictactoe/sinatra-tictactoe-ajax(master)]$ cat Procfile #web: bundle exec unicorn -p $PORT -E $RACK_ENV #web: bundle exec ruby app.rb -p $PORT web: bundle exec ruby app.rb #web: bundle exec thin startVéase The Procfile is your friend
[~/sinatra/sinatra-tictactoe/sinatra-tictactoe-ajax(master)]$ cat Gemfile source "https://rubygems.org" gem "sinatra" gem 'haml' gem "sass", :require => 'sass' gem 'thin'
[~/sinatra/sinatra-tictactoe/sinatra-tictactoe-ajax(master)]$ cat app.rb
require 'sinatra'
require 'sass'
require 'pp'
settings.port = ENV['PORT'] || 4567
enable :sessions
#use Rack::Session::Pool, :expire_after => 2592000
#set :session_secret, 'super secret'
#configure :development, :test do
# set :sessions, :domain => 'example.com'
#end
#configure :production do
# set :sessions, :domain => 'herokuapp.com'
#end
module TicTacToe
HUMAN = CIRCLE = "circle" # human
COMPUTER = CROSS = "cross" # computer
BLANK = ""
HORIZONTALS = [ %w{a1 a2 a3}, %w{b1 b2 b3}, %w{c1 c2 c3} ]
COLUMNS = [ %w{a1 b1 c1}, %w{a2 b2 c2}, %w{a3 b3 c3} ]
DIAGONALS = [ %w{a1 b2 c3}, %w{a3 b2 c1} ]
ROWS = HORIZONTALS + COLUMNS + DIAGONALS
MOVES = %w{a1 a2 a3 b1 b2 b3 c1 c2 c3}
def number_of(symbol, row)
row.find_all{ |s| session["bs"][s] == symbol }.size
end
def inicializa
@board = {}
MOVES.each do |k|
@board[k] = BLANK
end
@board
end
def board
session["bs"]
end
def [] key
board[key]
end
def []= key, value
board[key] = value
end
def each
MOVES.each do |move|
yield move
end
end
def legal_moves
m = []
MOVES.each do |key|
m << key if board[key] == BLANK
end
puts "legal_moves: Tablero: #{board.inspect}"
puts "legal_moves: m: #{m}"
m # returns the set of feasible moves [ "b3", "c2", ... ]
end
def winner
ROWS.each do |row|
circles = number_of(CIRCLE, row)
puts "winner: circles=#{circles}"
return CIRCLE if circles == 3 # "circle" wins
crosses = number_of(CROSS, row)
puts "winner: crosses=#{crosses}"
return CROSS if crosses == 3
end
false
end
def smart_move
moves = legal_moves
ROWS.each do |row|
if (number_of(BLANK, row) == 1) then
if (number_of(CROSS, row) == 2) then # If I have a win, take it.
row.each do |e|
return e if board[e] == BLANK
end
end
end
end
ROWS.each do |row|
if (number_of(BLANK, row) == 1) then
if (number_of(CIRCLE,row) == 2) then # If he is threatening to win, stop it.
row.each do |e|
return e if board[e] == BLANK
end
end
end
end
# Take the center if open.
return "b2" if moves.include? "b2"
# Defend opposite corners.
if self["a1"] != COMPUTER and self["a1"] != BLANK and self["c3"] == BLANK
return "c3"
elsif self["c3"] != COMPUTER and self["c3"] != BLANK and self["a1"] == BLANK
return "a1"
elsif self["a3"] != COMPUTER and self["a3"] != BLANK and self["c1"] == BLANK
return "c1"
elsif self["c1"] != COMPUTER and self["c3"] != BLANK and self["a3"] == BLANK
return "a3"
end
# Or make a random move.
moves[rand(moves.size)]
end
def human_wins?
winner == HUMAN
end
def computer_wins?
winner == COMPUTER
end
end
helpers TicTacToe
get %r{^/([abc][123])?$} do |human|
if human then
puts "You played: #{human}!"
puts "session: "
pp session
if legal_moves.include? human
board[human] = TicTacToe::CIRCLE
# computer = board.legal_moves.sample
computer = smart_move
redirect to ('/humanwins') if human_wins?
redirect to('/') unless computer
board[computer] = TicTacToe::CROSS
puts "I played: #{computer}!"
puts "Tablero: #{board.inspect}"
redirect to ('/computerwins') if computer_wins?
end
else
session["bs"] = inicializa()
puts "session = "
pp session
end
haml :game, :locals => { :b => board, :m => '' }
end
get '/humanwins' do
puts "/humanwins session="
pp session
begin
m = if human_wins? then
'Human wins'
else
redirect '/'
end
haml :final, :locals => { :b => board, :m => m }
rescue
redirect '/'
end
end
get '/computerwins' do
puts "/computerwins"
pp session
begin
m = if computer_wins? then
'Computer wins'
else
redirect '/'
end
haml :final, :locals => { :b => board, :m => m }
rescue
redirect '/'
end
end
not_found do
puts "not found!!!!!!!!!!!"
session["bs"] = inicializa()
haml :game, :locals => { :b => board, :m => 'Let us start a new game' }
end
get '/styles.css' do
scss :styles
end