I wrote a computer program and it showed that $18$ moves is the optimum.
Here is one such solution:
Oddly enough, even if you relax the condition of alternating white and black moves, it cannot be done in fewer moves.
For $3\times3$ the optimal number of moves is $16$.
Without the need to alternate moves the optimum is $14$ moves, for example just by doing the above solution excluding white's last two moves.
Here is the C# source code that I wrote.
using System;using System.Collections.Generic;namespace test{ class Rooks { static void Main() { Calc(true,4); } static void Calc(bool alternateMoves, int n ) { int[] dirs = {0, 1, 0, -1, 1, 0, -1, 0}; List<String> list = new List<String>(); Dictionary<String, String> dict = new Dictionary<String, String>(); string start = new string('b', n) + new string('.', n * (n - 2)) + new string('w', n); if (alternateMoves) start += '0'; string goal = new string('w', n) + new string('.', n * (n - 2)) + new string('b', n); list.Add(start); dict.Add(start, ""); int n1 = list.Count; int n2 = 0; int len = 0; while (list.Count > 0) { String p = list[0]; list.RemoveAt(0); n1--; String gen = dict[p]; char player = alternateMoves ? (p[n * n] == '0' ? 'w' : 'b') : '.'; for (int y = 0; y < n; y++) { for (int x = 0; x < n; x++) { if (!alternateMoves ^ p[y * n + x] == player) { for (int d = 0; d < 4; d++) { int dx = dirs[d + d]; int dy = dirs[d + d + 1]; int x2 = x; int y2 = y; while (true) { x2 += dx; y2 += dy; if (y2 < 0 || x2 < 0 || y2 >= n || x2 >= n || p[y2 * n + x2] != '.') break; string q = SwapPieces(p, y * n + x, y2 * n + x2); if(alternateMoves) q = q.Substring(0, n * n) + (char) (q[n * n] ^ 1); if (!dict.ContainsKey(q)) { list.Add(q); string gen2 = gen +""+ (char)('A'+ x) + (char)('1'+ y) + (char)('A'+ x2) + (char)('1'+ y2); dict.Add(q, gen2); if (q.StartsWith(goal)) { Console.WriteLine(q +""+ gen2); } n2++; } } } } } } if (n1 == 0) { len++; Console.WriteLine("{0}: {1}",len,n2); n1 = n2; n2 = 0; } } } static String SwapPieces(String input, int i1, int i2) { if (i1 > i2) return SwapPieces(input, i2, i1); return input.Substring(0, i1) + input.Substring(i2, 1) + input.Substring(i1 + 1, i2 - i1 - 1) + input.Substring(i1, 1) + input.Substring(i2 + 1); } }}