Те́трис (производное от «тетрамино» и «теннис»[2]) — компьютерная игра, первоначально изобретённая и разработанная советским программистом Алексеем Пажитновым. Игра была выпущена 6 июня 1984 года — в это время Пажитнов работал в Вычислительном центре Академии наук СССР.
«Тетрис» представляет собой головоломку, построенную на использовании геометрических фигур «тетрамино» — разновидности полимино, состоящих из четырёх квадратов. Полимино в том или ином виде использовались в настольных играх и головоломках задолго до создания «Тетриса». Идею «Тетриса» Пажитнову подсказала игра в пентамино[1]. Первоначальная версия игры была написана Пажитновым на языке Паскаль[3][4] для компьютера «Электроника-60». Коммерческая версия игры — первая из многих последующих — была выпущена американской компанией Spectrum HoloByte в 1987 году. В последующие годы «Тетрис» во множестве различных версий был портирован на великое множество устройств, включая все возможные компьютеры и игровые консоли, а также такие устройства, как графические калькуляторы, мобильные телефоны, медиа-плееры, карманные персональные компьютеры и — в качестве «пасхального яйца» — устройства, вовсе не предназначенные для воспроизведения медиа-контента, такие, как осциллограф и паяльник[5]. Ну а мы создадим реализацию для платы VE-EP4CE10E:
Но по-моему объяснения тут излишни, навряд ли найдется человек, который не знает, что такое Тетрис! Как только я увидел статью на habrahabr.ru, решил портировать проект на нашу плату VE-EP4CE10E.
Основной модуль написан на System Verilog, и представлен ниже:
SystemVerilog Code:
`include "./tetris/rtl/defs.vh" module top( input CLOCK_50, input [9:0] SW, ///////// PS2 ///////// inout PS2_CLK, inout PS2_DAT, ///////// VGA ///////// output [7:0] VGA_B, output VGA_BLANK_N, output VGA_CLK, output [7:0] VGA_G, output VGA_HS, output [7:0] VGA_R, output VGA_SYNC_N, // not used (?) output VGA_VS ); logic vga_clk; pll pll1( .inclk0 (CLOCK_50), .c0 (), .c1 (vga_clk), .c2 (), .c3 () ); assign VGA_CLK = vga_clk; logic main_reset; logic sw_0_d1; logic sw_0_d2; logic sw_0_d3; always_ff @( posedge CLOCK_50 ) begin sw_0_d1 <= SW[0]; sw_0_d2 <= sw_0_d1; sw_0_d3 <= sw_0_d2; end assign main_reset = sw_0_d3; logic [7:0] ps2_received_data_w; logic ps2_received_data_en_w; PS2_Controller ps2( .CLOCK_50 ( CLOCK_50 ), .reset ( main_reset ), // Bidirectionals .PS2_CLK ( PS2_CLK ), .PS2_DAT ( PS2_DAT ), .received_data ( ps2_received_data_w ), .received_data_en ( ps2_received_data_en_w ) ); logic user_event_rd_req_w; user_event_t user_event_w; logic user_event_ready_w; user_input user_input( .rst_i ( main_reset ), .ps2_clk_i ( CLOCK_50 ), .ps2_key_data_i ( ps2_received_data_w ), .ps2_key_data_en_i ( ps2_received_data_en_w ), .main_logic_clk_i ( vga_clk ), .user_event_rd_req_i ( user_event_rd_req_w ), .user_event_o ( user_event_w ), .user_event_ready_o ( user_event_ready_w ) ); game_data_t game_data_w; main_game_logic main_logic( .clk_i ( vga_clk ), .rst_i ( main_reset ), .user_event_i ( user_event_w ), .user_event_ready_i ( user_event_ready_w ), .user_event_rd_req_o ( user_event_rd_req_w ), .game_data_o ( game_data_w ) ); draw_tetris draw_tetris( .clk_vga_i ( vga_clk ), .game_data_i ( game_data_w ), // VGA interface .vga_hs_o ( VGA_HS ), .vga_vs_o ( VGA_VS ), .vga_de_o ( VGA_BLANK_N ), .vga_r_o ( VGA_R ), .vga_g_o ( VGA_G ), .vga_b_o ( VGA_B ) ); endmodule
FSM «общается» со следующими блоками/модулями:
- gen_sys_event — таймер, который отсчитывает время, через которое надо автоматически двинуть фигурку вниз.
- gen_next_block — генератор новой фигурки.
- check_move — проверка, можно ли выполнить текущий «ход».
- tetris_stat — накопление «статистики».
- user_input — считывает событие, которое «произвел» пользователь.
Всё очень похоже на «обычную» реализацию Тетриса, которые написаны на C++/Java/etc: различные модули выполняют роль функций в тех языках. Да и проблемы возникают такие же: дольше всего сидел над переворотом фигурки, ответ подсмотрел в коде quadrapassel. Один из вариантов является то, что можно хранить хранить таблицу всех возможных разворотов (для каждой фигурки четыре варианта).
Весь код написан на Verilog, а если быть более точным — на SystemVerilog. С одной стороны SystemVerilog намного гибче, чем Verilog, а с другой стороны это приводит к тому, что ты не ограничен, и хочешь всё больше и больше различных рюшечек реализовать :).
В принципе в оригинальной статье логика работы описана неплохо, поэтому мы ограничимся видео работы, и исходными кодами для нашей платы.
Проект игры Tetris: yafpgatetris.zip