Tạo game hangman
Bài đăng này đã không được cập nhật trong 3 năm
Tài liệu dịch: A Ruby Story
Trong bài viết này chúng tôi sẽ đi tạo một game hangman, đây là một game đơn giản mà bất kỳ một người chơi nào cũng có thể tìm ra từ mà ứng dụng của chúng tôi đã đưa ra bởi việc lựa chọn các chữ cái.
Chúng tôi sẽ sử dụng:
Rails (phiên bản 4): bạn sẽ thấy những gì hoàn toàn khác nếu bạn cài sai phiên bản. Foundation: cho looks & feels Font Awesome: cho icons
Tổng Quan:
Ứng dụng sẽ bao gồm 2 trang: 1. Welcome page - bắt đầu một game mới - tiếp tục game khi người dùng sẵn sàng bắt đầu 2. Game page - gallows container - một bộ chứa các chữ cái - một bộ chứa các ký tự la tinh - một button để quay trở lại home page và bắt đầu một game mới
Chúng tôi không dùng authentication/authorisation và không dùng active record để lưu trữ dữ liệu. Tất cả người dùng và trạng thái game sẽ lưu giữ trong session.
Code cho bài viết có sẵn trên Github
Hands on:
Hãy bắt đầu bằng việc tạo một ứng dụng rails và sử dụng -O để loại bỏ Active Record: rails new hangman -O
Bây giờ trong ứng dụng chúng tôi sẽ xóa đi những thứ không sử dụng:
Turbolinks: remove turbolinks từ Gemfile
mở file app/assets/javascripts/application.js
và remove đi dòng //=require turbolinks
mở app/views/layouts/application.html.erb
và remove đi 'data-turbolinks-track' => true
CoffeeScript:
remove đi gem coffee-rails từ Gemfile
sau khi đã thay đổi, mở terminal và chạy bundle từ thư mục root của ứng dụng để đảm bảo chúng ta không làm hỏng bất cứ thứ gì bundle
bây giờ sẽ bật server để kiểm tra tiến trình mà chúng ta đã thiết lập từ trước. Từ thư mục root của ứng dụng thực thi rails s
Controllers:
tạo ra 2 controller là HomeController và GamesController
rails generate controller home index rails generate controller games new show update destroy
Routes:
mở file config/routes.rb
và remove tất cả nội dung ở bên trong, sau đó thêm vào các dòng sau:
root to: 'home#index' resource :game, only: [:new, :show, :update, :destroy]`
Model:
chúng ta cần tạo ra một lớp Game để giứ trạng thái game (tạo ra một file game.rb
nằm trong thư mục models).
trong lớp Game cần làm những việc sau: 1. Xác định số lần lớn nhất của người chơi bị trượt. 2. Giữ những từ đã đoán được 3. Giữ những chữ cái đã được lựa chọn bởi người chơi 4. khởi tạo game 5. Trả lời số chữ cái đoán trượt 6. Trả lời nếu người chơi đoán được từ hay không 7. Trả lời nếu game là kết thúc hay chưa 8. Lựa chọn chữ cái
Xác định số lần lớn nhất của người chơi bị trượt:
Người chơi không thể cứ tiếp tục lựa chọn chữ cái mãi bởi vì như thế sẽ không phải là một trò chơi mà một bài tập luyện click chuột. Chúng ta phải xác định một gioi hạn số lận đoán trượt. Do đó thêm dòng sau vào lớp Game
MAX_FAILED_ATTEMPTS = 5
Giữ những từ đã được đoán:
thêm một attribute accessor cho wordattr_accessor :word
Giữ những chữ cái mà người chơi đã lựa chọn:
attr_accessor :selected_letters
Khởi tạo một game:
def initialize
@word = 'Hangman'.upcase
@selected_letters = []
end
Giờ tạm thời chúng ta lựa chọn từ Hangman nhưng sau chúng ta sẽ tạo ra một cơ chế cho phép lựa chọn từ ngẫu nhiên từ một nguồn cụ thể.
Trả lời số chữ cái đoán trượt:
def failed_attempts
selected_letters.select { |letter|
!word.include?(letter)
}.size
end
Ở đây chúng ta lặp qua các chữ cái được lựa chọn và đếm xem có bao nhiêu trong số chúng đã được bao gồm trong từ của game.
Trả lời nếu người chơi đã đoán được từ
def guessed?
(word.split('') - selected_letters).empty?
end
Ở đây chúng ta chuyển đổi word thành một mảng các ký tự của nó và sau đó chúng ta sẽ loại bỏ những chữ cái được lựa chọn từ mảng này. Nếu kết qủa là empty điều đó có nghĩa là người dùng đã lựa chọn tất cả các chữ cái của từ và người chơi đã đoán được từ.
Trả lời nếu trò trơi được kết thúc:
def finished?
failed_attempts >= MAX_FAILED_ATTEMPTS || guessed?
end
Ở đây chúng ta sẽ trả lời rằng một game đã kết thúc khi đã đạt đén giới hạn số lần đoán trượt hoặc người chơi đã đoán từ thành công.
Lựa chọn một chữ cái:
def select!(letter)
raise GameOverError if finished?
selected_letters << letter unless selected_letters.include? letter
word.include? letter
end
Ở đây chúng ta sẽ phát sinh ra lỗi trong trường hợp game kết thúc. Chúng ta sẽ xử lý lỗi này sau trong controller của chúng ta. Chúng ta sẽ add nhưng chữ cái được lựa chọn trong trạng thái của game. và sẽ đưa ra trả lời là đúng hay sai dựa trên việc có chứa hay không chữ cái này.
Chú ý:
chúng ta định nghĩa lớp GameOverError ở một nơi nào khác ngoài code của chúng ta là không hợp lệ.
bởi vậy hãy thêm dòng sau vào lớp Gameclass GameOverError < StandardError; end
Tiếp tục thêm code sau vào class game
include ActiveModel::Serializers::JSON
def attributes
{word: nil, selected_letters: nil}
end
def attributes=(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end
Đã đến lúc chúng ta cấu hình ứng dụng để sử dụng Foundation và Font Awesome để phục vụ cho việc tạo các views
Foundation:
chúng ta sẽ sử dụng Foundation bởi vì grid linh động tuyệt vời của nó.
qua đó sẽ sử dụng thêm một số tính năng hấp dẫn khác (như là các button styles & utilities).
đầu tiên cài đặt gem foundation-rails
tiếp theo bundle
kế tiếp, thiết lập nó cho ứng dụng thực thi như sau
rails generate foundation:install
tiếp đến khi chạy câu lệnh trên terminal có hỏi chúng ta có muốn ghi đè message thì nhấn enter tiếp tục, lúc đó Foundation đã được cài đặt thành công và thiết lập cho ứng dụng.
Font Awesome:
chúng tôi sử dụng font-awesome bởi vì các icons tuyệt vời của nó và thực tế chúng có thay đổi được kích thước và màu sắc theo quy luật kích thước font và màu sắc của css.
đầu tiên chúng ta cần cài đặt gem font-awesome-rails:
`gem 'font-awesome-rails'
thêm dòng sau vào file app/stylesheets/application.css
:
*= require font-awesome`
Bây giờ chúng ta có thể bắt đầu xây dựng views
Welcome page:
Giao diện của trang như hình dưới:
Thêm code sau tới file index.html.erb
<div class="row">
<div class="small-12 columns text-center">
<h1>Welcome to Hangman!</h1>
</div>
</div>
<div class="row">
<div class="medium-6 columns">
<button class="button expand">New game</button>
</div>
<div class="medium-6 columns">
<button class="button expand">Continue game</button>
</div>
</div>
Chúng tôi không sử dụng bất kỳ database nào mà chúng tôi sử dụng session để lưu trữ vì đây là một ví dụ cơ bản để thực hành còn trong các ứng dụng thực thì không nên dùng theo cách này.
Do đó mở file app/controllers/application_controller.rb
thêm vào code sau:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_game
def current_game
@current_game ||= load_current_game
end
def set_current_game(game)
@current_game = game
session[:serialized_current_game] = game.present? ? game.to_json : nil
end
def update_current_game
set_current_game @current_game
end
protected
def load_current_game
Game.new.from_json(session[:serialized_current_game]) if session[:serialized_current_game].present?
end
end
Chú ý:
current_game: load và giữ game hiện thời tới biến @current_game.
set_current_game: thiết lập cho biến @current_page và tên của game hiện thơi đã được serializer lưu trữ trong sesssion.
update_current_game: serialize game hiện thời tới session, chúng ta sẽ sử dụng nó phụ thuộc vào việc người dùng lựa chọn chữ cái.
Thay thề file index.html.erb
với code sau:
<div class="row">
<div class="small-12 columns text-center">
<h1>Welcome to Hangman!</h1>
</div>
</div>
<div class="row">
<div class="<%= current_game.present? && !current_game.finished? ? 'medium-6' : '' %> columns">
<%= link_to new_game_path, :class => 'button expand' do %>
<i class="fa fa-play"> New game</i>
<% end %>
</div>
<% if session[:serialized_current_game].present? %>
<div class="medium-6 columns">
<%= link_to game_path, :class => 'button expand' do %>
<i class="fa fa-refresh"> Continue game</i>
<% end %>
</div>
<% end %>
</div>
Reload lại page của trình duyệt
Ở đây khi nhấn button "New game" chúng ta chỉ cần tạo ra một game hangman mới, thiết lập session của người chơi giống như là game hiện thời và sau đó chuyển đến page show game.
Bởi vậy, xóa đi file phát sinh tự động app/views/games/new.html.erb
và thay đổi action new trong controller games như sau:
def new
set_current_game Game.new
redirect_to game_path
end
Bây giờ, nhấn button "New game" sẽ chuyển sang page show game
Show page: Giao diện như sau:
Cho gallows, chúng ta sử dụng 6 cái ảnh (download chúng ở đây, sau đó thêm tới app/assets/images
)
Bây giờ mở file app/views/games/show.html.erb
và thay thế nội dung bên trong bằng code dưới đây:
<div class="row">
<div class="medium-4 columns">
<div id="gallows" class="gallows gallows-state-<%= current_game.failed_attempts %>">
</div>
</div>
<div class="medium-8 columns">
<div class="row">
<div class="small-12 columns">
<ul class="word small-block-grid-<%= current_game.word.length %>">
<% current_game.word.split('').each do |letter| %>
<li>
<div class="word-letter">
<%= current_game.finished? || current_game.selected_letters.include?(letter) ? letter : '_' %>
</div>
</li>
<% end %>
</ul>
</div>
</div>
<% if current_game.finished? %>
<div class="row game-status">
<div class="small-12 columns text-center">
<% if current_game.guessed? %>
<span class="label success radius">You successfully guessed the word! :)</span>
<% else %>
<span class="label alert radius">No, no... You didn't find the word :(</span>
<% end %>
</div>
</div>
<% end %>
<%= form_for :game, :url => game_path, :method => :patch do |form| %>
<div class="row">
<div class="letters">
<% ('A'..'Z').each do |letter| %>
<%
if current_game.selected_letters.include? letter
button_class = current_game.word.include?(letter) ? 'success' : 'alert'
end
%>
<div class="medium-2 columns text-center">
<div class="letter">
<%= form.submit letter, :name => 'letter', :class => "button expand #{button_class}" %>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
<div class="row">
<div class="game-actions">
<div class="<%= current_game.finished? ? '' : 'medium-6' %> columns">
<%= link_to root_path(:method => :delete), :class => 'button expand' do %>
<i class="fa fa-home"></i> Home
<% end %>
</div>
<% unless current_game.finished? %>
<div class="medium-6 columns">
<%= link_to game_path, :class => 'button expand alert', :method => :delete do %>
<i class="fa fa-fire"></i> Cancel game
<% end %>
</div>
<% end %>
</div>
</div>
</div>
</div>
Chỉnh sửa file app/controllers/games_controller.rb
và thêm vào 2 phương thức như sau:
def update
current_game.select! params[:letter]
update_current_game
rescue Game::GameOverError
flash[:alert] = 'This game is finished...'
ensure
redirect_to game_path
end
def destroy
set_current_game nil
redirect_to root_path
end
Reload lại page giờ chúng ta có thể chơi game:
All rights reserved