2008 November 20th Thursday (十一月 二十日 木曜日)

本文介绍了一个使用Erlang实现的俄罗斯方块游戏示例代码,该示例从官方网站获取,虽存在一些小错误但十分有趣。游戏通过定时器控制方块下落速度并实现了分数显示等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   I got a tetris game in Erlang from the Erlang offical site.  The example has some bugs, but it is interesting.

%% start the timer at 750 ms
%% then reduce by 6 ms every 10 seconds (i.e. 60 ms /min)
%% i.e. down to zero in 12.5 mins
%% reduce by 50 ms every time we go through a 25 boundary

-define(TimerInterval, 10000). %% every ten seconds
-define(DeltaTime,         6). %% reduce by 6 ms

start(0) -> true;
start(N) -> start(20, 750),start(N-1).

start(Size, Time) -> spawn(?MODULE, internal, [Size, Time]).

internal(Size, Time) ->
    W = 10*Size,
    H = 20*Size + 40,
    S = gs:start(),
    Win = gs:create(window, S, [{title, "Tetris"},
{width,W}, {height,H}, {bg,pink},
{keypress, true}]),
    put(quit, gs:create(button, quit,Win,[{label, {text,"quit"}}
  ,{width,80},{height,25},
  {x,10},{y,H-30}])),
    put(score, gs:create(label, Win, [{label, {text, "0"}},
      {width, 40}, {height,25},
      {x,100}, {y,H-30}])),
    put(theScore, 0),
    random_start(),
    make_squares(Win, Size),
    gs:config(Win, {map, true}),
    Piece = new_piece(Win),
    Pinger = spawn_link(?MODULE, internal_pinger, [Time, self()]),
    put(pinger, Pinger),
    spawn_link(?MODULE,  internal_speed_up, [Pinger]),
    loop(Win, Piece, []).

make_squares(Win, Size) ->
    foreach(fun(Y) ->
   foreach(fun(X) ->
  YY = (20-X)*Size,
  XX = (Y-1) * Size,
  B = gs:create(label,Win,
    [{x,XX},{y,YY},{bg,pink},
     {width,Size-1},{height, Size-1}]),
  put({X,Y}, B)
  end, seq(1,20))
    end, seq(1,10)).

loop(W, Bit, State) ->
    receive
{gs,quit,_,_,_}                -> true;
{gs,W,keypress,[],[b|_]}     -> move(left,  W, Bit, State);
{gs,W,keypress,[],[n|_]}     -> move(rot,   W, Bit, State);
{gs,W,keypress,[],[m|_]}     -> move(right, W, Bit, State);
drop_block                     -> move(down,  W, Bit, State);
{gs,W,keypress,[],[space|_]} -> move(drop,  W, Bit, State);
{gs,W,keypress,[],[d|_]}     -> move(down,  W, Bit, State);
{gs,W,keypress,[],[question|_]} ->
    io:format("b -- left/nn -- rotate/nm -- right/nspace -- drop/n"),
    loop(W, Bit, State);
Any ->
    io:format("? for help/n"),
    loop(W, Bit, State)
    end.

%%______________________________________________________________________
%% new_pos(Direction, Piece) -> Piece'

new_pos(down,  {P, Col, X, Y, R}) -> {P, Col, X-1, Y,   R};
new_pos(left,  {P, Col, X, Y, R}) -> {P, Col, X,   Y-1, R};
new_pos(right, {P, Col, X, Y, R}) -> {P, Col, X,   Y+1, R};
new_pos(rot,   {P, Col, X, Y, 4}) -> {P, Col, X,   Y,   1};
new_pos(rot,   {P, Col, X, Y, R}) -> {P, Col, X,   Y,   R+1};
new_pos(drop,  Piece)             -> new_pos(down, Piece).

%%______________________________________________________________________
%% move(Dirn, Win, Old, State)
%%  tries to move the piece Old in the direction Dirn

move(Dirn, Win, Old, State) ->
    New  = new_pos(Dirn, Old),
    %% io:format("now ~p/n", [New]),
    case can_piece_move(New, State) of
true ->
    draw(Win, visable(Old), pink),
    draw(Win, visable(New), color(New)),
    case Dirn of
drop -> move(Dirn, Win, New, State);
_    -> loop(Win, New, State)
    end;
false when Dirn == down ->
    freeze(Win, Old, State);
false when Dirn == drop ->
    freeze(Win, Old, State);
false ->
    loop(Win, Old, State)
    end.

%%______________________________________________________________________
%% freeze(Win, Piece, State)
%%   gets called when Piece can no longer move

freeze(Win, Piece, State) ->
    {_,Color,_,_,_} = Piece,
    State1 = map(fun(X) -> {X,Color} end, covered(Piece)) ++ State,
    State2 = remove_full_rows(Win, State1),
    case State2 of
[{{Row,_},_}|_] when Row > 20 ->
    tetris_high_score_show("tetris.dat", get(theScore)),
    game_over(Win);
_ ->
    loop(Win, new_piece(Win), State2)
    end.

%%______________________________________________________________________
%% remove_full_rows(Win, State) -> State'
%%   removes full rows after a piece has stopped falling
%%   updates the score

remove_full_rows(Win, State) ->
    S1 = sort(State),
    remove_full_row(S1, Win, []).

remove_full_row([{{X,1},_},{{X,2},_},{{X,3},_},{{X,4},_},{{X,5},_},
{{X,6},_},{{X,7},_},{{X,8},_},{{X,9},_},{{X,10},_}|T], Win,
       L) ->
    draw(Win, [{X,1},{X,2},{X,3},{X,4},{X,5},{X,6},{X,7},{X,8},
       {X,9},{X,10}], pink),
    update_score(),
    L1 = shift_rows(T, Win, L),
    remove_full_rows(Win, L1);
remove_full_row([H|T], Win, L) ->
    remove_full_row(T, Win, [H|L]);
remove_full_row([], Win, L) ->
    L.

shift_rows([{{X,Y},Col}|T], Win, L) ->
    draw(Win, [{X,Y}], pink),
    draw(Win, [{X-1,Y}], Col),
    shift_rows(T, Win, [{{X-1,Y},Col}|L]);
shift_rows([], Win, L) ->
    L.

%%______________________________________________________________________
%% can_piece_move(Piece, State) -> Bool

can_piece_move(Piece, State) ->
    Covered = covered(Piece),
    aand(nnot(any(fun(Sq) -> keymember(Sq, 1, State) end, Covered)),
nnot(hit_side(Covered))).

%%______________________________________________________________________
%% hit_side([{X,Y}]) -> Bool

hit_side([{0,_}|_])  -> true;
hit_side([{_,0}|_])  -> true;
hit_side([{_,11}|_]) -> true;
hit_side([_|T])      -> hit_side(T);
hit_side([])         -> false.

%%______________________________________________________________________
%% start_cols(Shape, Rot) -> {MinCol, MaxCol}
%%%  given a  shape and a rotation compute the range of columns that
%%%  the the piece can start in

start_cols(tee, 2) -> {1,9};
start_cols(tee, 4) -> {2,10};
start_cols(bar, 1) -> {1,10};
start_cols(bar, 2) -> {3,9};
start_cols(bar, 3) -> {2,10};
start_cols(bar, 4) -> {3, 9};
start_cols(box, _) -> {1,9};
start_cols(_, _)   -> {2, 9}.

%%----------------------------------------------------------------------
%% new_piece(Win) -> Piece
%%   generate a new piece and update the display

new_piece(Win) ->
    Shape = random_shape(),
    Rot   = random(1, 4),
    {Min, Max} = start_cols(Shape, Rot),
    Col = random(Min, Max),
    Piece =  {Shape, random_color(), 22, Col, Rot},
    draw_piece(Win, Piece),
    Piece.

%%______________________________________________________________________
%% draw_piece(Win, Piece).

draw_piece(Win, Piece) ->
    draw(Win, visable(Piece), color(Piece)).

draw(Win, Squares, Color) ->
    foreach(fun({I,J}) ->
   gs:config(get({I,J}), [{bg, Color}])
   end, Squares).

%%----------------------------------------------------------------------
%% visible(Piece) -> [{X,Y}]
%%   a list of the squares which are covered and on the board

visable(B) ->  filter(fun(X) -> on_board(X) end, covered(B)).

%%______________________________________________________________________
%% covered(Piece) -> [{X,Y}]
%%   a list of the pieces which are covered

covered({tee,_,X,Y,1}) -> [{X,Y},{X-1,Y-1},{X-1,Y},{X-1,Y+1}];
covered({tee,_,X,Y,2}) -> [{X,Y},{X,Y+1},{X+1,Y+1},{X-1,Y+1}];
covered({tee,_,X,Y,3}) -> [{X,Y},{X+1,Y},{X+1,Y+1},{X+1,Y-1}];
covered({tee,_,X,Y,4}) -> [{X,Y},{X,Y-1},{X+1,Y-1},{X-1,Y-1}];
covered({l1,_,X,Y,1})  -> [{X+1,Y-1},{X+1,Y},{X+1,Y+1},{X,Y+1}];
covered({l1,_,X,Y,2})  -> [{X-1,Y-1},{X,Y-1},{X+1,Y-1},{X+1,Y}];
covered({l1,_,X,Y,3})  -> [{X-1,Y-1},{X-1,Y},{X-1,Y+1},{X,Y-1}];
covered({l1,_,X,Y,4})  -> [{X-1,Y+1},{X,Y+1},{X+1,Y+1},{X-1,Y}];
covered({l2,_,X,Y,1})  -> [{X+1,Y-1},{X+1,Y},{X+1,Y+1},{X,Y-1}];
covered({l2,_,X,Y,2})  -> [{X-1,Y-1},{X,Y-1},{X+1,Y-1},{X-1,Y}];
covered({l2,_,X,Y,3})  -> [{X-1,Y-1},{X-1,Y},{X-1,Y+1},{X,Y+1}];
covered({l2,_,X,Y,4})  -> [{X-1,Y+1},{X,Y+1},{X+1,Y+1},{X+1,Y}];
covered({bar,_,X,Y,1}) -> [{X,Y},{X-1,Y},{X+1,Y},{X+2,Y}];
covered({bar,_,X,Y,2}) -> [{X+1,Y},{X+1,Y+1},{X+1,Y-1},{X+1,Y-2}];
covered({bar,_,X,Y,3}) -> [{X+2,Y-1},{X-1,Y-1},{X,Y-1},{X+1,Y-1}];
covered({bar,_,X,Y,4}) -> [{X,Y-2},{X,Y-1},{X,Y},{X,Y+1}];
covered({r1,_,X,Y,1})  -> [{X,Y},{X+1,Y},{X,Y+1},{X-1,Y+1}];
covered({r1,_,X,Y,2})  -> [{X,Y},{X,Y-1},{X+1,Y},{X+1,Y+1}];
covered({r1,_,X,Y,3})  -> [{X,Y},{X+1,Y-1},{X,Y-1},{X-1,Y}];
covered({r1,_,X,Y,4})  -> [{X,Y},{X-1,Y-1},{X-1,Y},{X,Y+1}];
covered({r2,_,X,Y,1})  -> [{X,Y},{X+1,Y},{X-1,Y-1},{X,Y-1}];
covered({r2,_,X,Y,2})  -> [{X,Y},{X,Y-1},{X-1,Y+1},{X-1,Y}];
covered({r2,_,X,Y,3})  -> [{X,Y},{X-1,Y},{X+1,Y+1},{X,Y+1}];
covered({r2,_,X,Y,4})  -> [{X,Y},{X,Y+1},{X+1,Y},{X+1,Y-1}];
covered({box,_,X,Y,_})  -> [{X,Y},{X,Y+1},{X+1,Y},{X+1,Y+1}].

%%______________________________________________________________________
%% onboard({X,Y}) -> Bool
%%   test if a square is on the board

on_board({X,Y}) when 1 =< X, X =< 20, 1 =< Y, Y =< 10 -> true;
on_board(_)     -> false.

%%______________________________________________________________________
%% internal_pinger(T, Pid)
%%   every T ms sends a ping messaage to Pid
%%   the ping message will cause a block to drop

internal_pinger(T, Pid) ->
    receive
{reduce, Delta} ->
    internal_pinger(reduce_time(T - Delta), Pid)
    after T ->
    Pid ! drop_block,
    internal_pinger(T, Pid)
    end.

reduce_time(X) when X < 50 -> 50;
reduce_time(X) -> X.

%%______________________________________________________________________
%% internal_speed_up(Pid)
%%   every ?TimerInteral ms tell Pid to speed up by
%%   ?DeltaTime ms.

internal_speed_up(Pid) ->
    receive
    after ?TimerInterval ->
    Pid ! {reduce, ?DeltaTime}
    end,
    internal_speed_up(Pid).

%%______________________________________________________________________
%% update_score()
%%  
%% every time bump the score by 10 and update the score
%% when we go through a 200 boundary (20 rows)
%% speed the clock up by 50 ms.

update_score() ->
    S = get(theScore) + 10,
    put(theScore, S),
    gs:config(get(score), [{label, {text, integer_to_list(S)}}]),
    case S rem 200 of
0 -> get(pinger) ! {reduce, 50};
_ -> true
    end.

%%______________________________________________________________________
%%

game_over(Win) ->
    gs:config(get(quit), [{label, {text, "Game over"}}]),
    wait_quit(Win).

wait_quit(Win) ->
    receive
{gs, quit, _, _, _} ->
    gs:destroy(Win),
    exit(die);
Any ->
    wait_quit(Win)
    end.   

%%______________________________________________________________________
%% random things

random_start() ->
    {H,M,S} = erlang:now(),
    random:seed(S,H,M).

random_shape() -> random([r1,r2,l1,l2,tee,tee,bar,bar,box,box]).

random_color() -> random([red,blue,green,yellow,black,orange]). %% not pink !!

random(L) -> nth(random:uniform(length(L)), L).

random(Min, Max) -> Min + random:uniform(Max-Min+1) - 1.

%%______________________________________________________________________
%% obvious !

any(F, [H|T]) ->
    case F(H) of
true -> true;
false -> any(F, T)
    end;
any(F, []) -> false.

aand(true, true) -> true;
aand(_,_)        -> false.

nnot(true) -> false;
nnot(false) -> true.

color({_,Color,_,_,_}) -> Color.


%% Usage   : tetris_high_score_show(File)
%%         :     pops up a high score table
%%         : tetris_high_score_show(File, Score)
%%         :     possible add Score to the high score table
%%         :     prompting if necessary
%%         : The high score table is scored in File

show_high_score() -> tetris_high_score_show("tetris.dat").

tetris_high_score_show(File) -> spawn_link(?MODULE, show1, [File]).

tetris_high_score_show(File, Score) ->
    spawn_link(?MODULE, show2, [File, Score]).

show1(File) ->
    case file:read_file(File) of
{error, _} ->
    display_scores([]);
{ok, Bin} ->
    display_scores(binary_to_term(Bin))
    end.

show2(File, Score) ->
    case file:read_file(File) of
{error, _} ->
    add_score(File, Score, []);
{ok, Bin} ->
    T = binary_to_term(Bin),
    case add_to_hs(Score, T) of
true ->
    add_score(File, Score, T);
false ->
    display_scores(T)
    end
    end.

add_to_hs(_, T) when length(T) < 10 -> true;
add_to_hs(Score, T) ->
    case reverse(T) of
[{Min,_,_}|_] when Score > Min ->
    true;
_  ->
    false
    end.

add_score(File, Score, T) ->
    P = self(),
    ask("New high score!",
"Name:",
fun(Str) -> P ! {name,Str} end),
    receive
{name, Str} ->
    T1 = trim(merge(Score, Str, T)),
    file:write_file(File, term_to_binary(T1)),
    display_scores(T1)
    end.

display_scores(T) ->
    Texts = map(fun({Score,Name,{Y,M,D}}) ->
       [integer_to_list(Score),
Name,
flatten(io_lib:format("~4w-~2.2.0w-~2.2.0w",
      [Y,M,D]))]
       end, T),
    display(Texts).

merge(Score, Name, []) -> [{Score,Name,date()}];
merge(Score, Name, [{S,N,D}|T]) when Score > S->
    [{Score,Name,date()},{S,N,D}|T];
merge(Score, Name, [H|T]) ->
    [H|merge(Score,Name,T)].

trim([X1,X2,X3,X4,X5,X6,X7,X8,X9,X10|_]) ->
    [X1,X2,X3,X4,X5,X6,X7,X8,X9,X10];
trim(X) ->
    X.

display(Text) ->
    spawn_link(?MODULE, internal_1, [Text]).

internal_1(Text) ->
    W = 365,
    H = 255,
    ColWidths = [60,200,100],
    Title = "Tetris hall of fame",
    S = gs:start(),
    Win = gs:create(window, S, [{width,W}, {height,H}, {title, Title}]),
    G   = gs:create(grid, Win, [{width, W-10},{height,H-30}, {x,2}, {y,2},
{columnwidths, ColWidths},{hscroll,false},
{vscroll, false},
{rows, {1,10}}]),
    gs:create(button, ok, Win, [{label,{text,"dismiss"}},
{width,100},{y,H-35}]),
    print(Text, 1, G),
    gs:config(Win, {map, true}),
    wait(Win).

print([], _, _) -> true;
print([[Score,Name,Date]|T], N, G) ->
    gs:create(gridline, G, [{row, N},
    {text,{1,Score}},{text,{2,Name}},
    {text,{3,Date}}]),
    print(T, N+1, G).


wait(Win) ->
    receive
{gs,ok, _,_,_} ->
    gs:destroy(Win);
Any ->
    io:format("got:~p/n", [Any]),
    wait(Win)
    end.

ask(Title, Text, Fun) ->
    spawn_link(?MODULE, ask3, [Title, Text, Fun]).

ask3(Title, Text, Fun) ->
    S = gs:start(),
    Win = gs:create(window, S, [{width,260}, {height,75}, {title,Title}]),
    gs:create(button, ok,Win,[{label, {text,"ok"}},{width,80},{height,25},
      {x,10},{y,40}]),
    gs:create(label, Win, [{label, {text, Text}}, {width, 40}, {height,25},
   {x,10}, {y,10}]),
    E = gs:create(entry, Win, [{width, 170}, {height, 25},
       {x,70}, {y,10}]),
    gs:config(Win, {map, true}),
    wait(Win, E, Fun).

wait(Win, E, Fun) ->
    receive
{gs,ok, _,_,_} ->
    Str = gs:read(E, text),
    gs:destroy(Win),
    Fun(Str);
Any ->
    io:format("got:~p/n", [Any]),
    wait(Win, E, Fun)
    end.

  The above example can launch n game instances.  You just type tetris:start(n) in the erl shell.  :-)


资源下载链接为: https://pan.quark.cn/s/d9ef5828b597 在本文中,我们将探讨如何通过 Vue.js 实现一个带有动画效果的“回到顶部”功能。Vue.js 是一款用于构建用户界面的流行 JavaScript 框架,其组件化和响应式设计让实现这种交互功能变得十分便捷。 首先,我们来分析 HTML 代码。在这个示例中,存在一个 ID 为 back-to-top 的 div 元素,其中包含两个 span 标签,分别显示“回到”和“顶部”文字。该 div 元素绑定了 Vue.js 的 @click 事件处理器 backToTop,用于处理点击事件,同时还绑定了 v-show 指令来控制按钮的显示与隐藏。v-cloak 指令的作用是在 Vue 实例渲染完成之前隐藏该元素,避免出现闪烁现象。 CSS 部分(backTop.css)主要负责样式设计。它首先清除了一些默认的边距和填充,对 html 和 body 进行了全屏布局,并设置了相对定位。.back-to-top 类则定义了“回到顶部”按钮的样式,包括其位置、圆角、阴影、填充以及悬停时背景颜色的变化。此外,与 v-cloak 相关的 CSS 确保在 Vue 实例加载过程中隐藏该元素。每个 .page 类代表一个页面,每个页面的高度设置为 400px,用于模拟多页面的滚动效果。 接下来是 JavaScript 部分(backTop.js)。在这里,我们创建了一个 Vue 实例。实例的 el 属性指定 Vue 将挂载到的 DOM 元素(#back-to-top)。data 对象中包含三个属性:backTopShow 用于控制按钮的显示状态;backTopAllow 用于防止用户快速连续点击;backSeconds 定义了回到顶部所需的时间;showPx 则规定了滚动多少像素后显示“回到顶部”按钮。 在 V
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值