require "curses"
include Curses

class Playfield < Window

	def draw_vertical_line(x, y_start, y_finish)
		for y in y_start..y_finish
			setpos(y, x)
			addch '|'
		end
	end

	def draw_horizontal_line(y, x_start, x_finish)
		for x in x_start..x_finish
			setpos(y, x)
			addch '_'
			setpos(y, x + 7)
			addch '_'
			setpos(y, x + 14)
			addch '_'
		end
	end

	def move_cursor(ch, pos)
		case ch
		when "\167"
			pos[:y] -= 3 if pos[:y] > 1
			setpos(pos[:y], pos[:x])
		when "\163"
			pos[:y] += 3 if pos[:y] < 7
			setpos(pos[:y], pos[:x])
		when "\141"
			pos[:x] -= 7 if pos[:x] > 3
			setpos(pos[:y], pos[:x])
		when "\144"
			pos[:x] += 7 if pos[:x] < 17
			setpos(pos[:y], pos[:x])
		end
	end

	def place_mark(mark, marks, pos)
		if inch == ' '.ord
			addch(mark)
			marks.each do |line| 
				line.each do |element|
					element[:value] = mark if element[:y] == pos[:y] && element[:x] == pos[:x]
				end
			end
			setpos(pos[:y], pos[:x])
			return 'OK'
		else
			return nil
		end
	end

	def check_marks(marks, mark)
		# horizontal
		marks.each do |line|
			return 'win' if line.count{|element| element[:value] == mark} == 3
		end

		# vertical
		transposed_marks = marks.transpose
		transposed_marks.each do |column|
			return 'win' if column.count{|element| element[:value] == mark} == 3
		end

		# diagonal
		return 'win' if marks[0][0][:value] == mark && marks[1][1][:value] == mark && marks[2][2][:value] == mark
		return 'win' if marks[2][0][:value] == mark && marks[1][1][:value] == mark && marks[0][2][:value] == mark

		# draw?
		counter = 0
		marks.each do |line|
			line.each { |element| counter += 1 if element[:value] == ' ' }
		end
		return "draw" if counter == 0

		return nil
	end

end


init_screen
crmode
noecho

# controls
message = "Press W, A, S, D to navigate the cursor, press Space to place your mark."
c = Window.new(1, message.length, lines/2 + 11, 0)
c.addstr(message)
c.refresh

# hints
h = Window.new(3, 38, lines/2 + 6, cols/2 - 18)
h.addstr "player 1's turn".ljust(38)
h.refresh


# playfield
p = Playfield.new(9, 20, lines/2 - 5, cols/2 - 9)

# draw grid
p.draw_vertical_line(6, 0, 8)
p.draw_vertical_line(13, 0, 8)
p.draw_horizontal_line(2, 0, 5)
p.draw_horizontal_line(5, 0, 5)

# kostyl
cursor_pos = { y: 4, x: 10 }
p.setpos(cursor_pos[:y], cursor_pos[:x])

# array of marks with coordinates
y = [1, 4, 7]
x = [3, 10, 17]
marks = Array.new(3) { Array.new(3) }
marks.each_index do |line| 
	marks[line].each_index do |column|
		marks[line][column] = { y: y[line], x: x[column], value: ' ' }
	end
end

# game part
mark = []
if rand(1..2) == 1
	mark[1] = 'X'
	mark[2] = 'O'
else
	mark[1] = 'O'
	mark[2] = 'X'
end

turn = 1 # 1st player's turn
begin
	begin
		ch = p.getch
		p.move_cursor(ch, cursor_pos)
	end until ch == ?\s
	if p.place_mark(mark[turn], marks, cursor_pos)
		result = p.check_marks(marks, mark[turn])
		unless result
			h.setpos(0, 0)
			if turn == 1
				turn = 2
				h.addstr "player #{turn}'s turn".rjust(38)
			else
				turn = 1
				h.addstr "player #{turn}'s turn".ljust(38)
			end
			h.refresh
		end
	end
end until result

h.setpos(0, 0)
if result == "draw"
	h.addstr "DRAW!".center(38)
else
	h.addstr "PLAYER #{turn} WINS!".center(38)
end
h.setpos(2, 0)
h.addstr "Press any key to exit.".center(38)
h.refresh

p.getch
p.close

close_screen
